Option Explicit

' ======================================================================
' Descriptor
' SqlFormatter格式化器的解析器
' ======================================================================

' ======================================================================
' Field
' ======================================================================

' ==================================================
' 分析/语法阶段需要的内容
' ==================================================
' 多个 sql 的 token 的缓存
' 数组中的每个 IObjArrayRef<SqlToken> 元素表示一个 sql 的 token
' 每一个 sql 的token中不包含分隔的 `;`
' 在生成结果时，默认会在每个 sql 后面添加一个 `;`
' IObjArrayRef<IObjArrayRef<SqlToken>>
Private multiSqlMeanTokenCache As IObjArrayRef

' sql 的解析结果 ---> 作用域
' IObjArrayRef<ISqlFormatterElem>
Private sqlEnvCache As IObjArrayRef

' sql token 提取器
' SqlTokenExtractor
Private tokenExtractor As SqlTokenExtractor

' ======================================================================
' 辅助对象: 各类型 sql 的组成部分的解析器
' ======================================================================
' 1. select sql 组成部分的解析器
Private selectAnalyzer As SqlFormatterSelectSubEAnalyzer
' 2. update sql 组成部分的解析器
Private updateAnalyzer As SqlFormatterUpdateSubEAnalyzer
' 3. delete sql 组成部分的解析器
Private deleteAnalyzer As SqlFormatterDeleteSubEAnalyzer
' 4. insert sql 组成部分的解析器
Private insertAnalyzer As SqlFormatterInsertSubEAnalyzer

' ==================================================
' 初始化时需要加载的内容
' ==================================================
Private config As SqlFormatterConfiguration

' ======================================================================
' 构造器
' ======================================================================
Private Sub Class_Terminate()
    Set multiSqlMeanTokenCache = Nothing
    Set sqlEnvCache = Nothing
    Set tokenExtractor = Nothing

    Set selectAnalyzer = Nothing
    Set updateAnalyzer = Nothing
    Set deleteAnalyzer = Nothing
    Set insertAnalyzer = Nothing

    Set config = Nothing
End Sub

' ======================================================================
' 初始化
' ======================================================================

' 初始化 SqlFormatter
'
' @param configVal 格式化配置对象
Public Sub Init(ByRef configVal As SqlFormatterConfiguration)
    ' 保存配置对象
    Set config = configVal

    ' sql token 提取器不在这里创建，在第一次调用 analyze 方法时，通过 extractorSqlToken 来创建
End Sub

' ======================================================================
' Public Function
' ======================================================================
' 获取当前解析使用的配置对象
Public Function getConfig() As SqlFormatterConfiguration
    Set getConfig = config
End Function

' 返回解析结果作用域
'
' @return IObjArrayRef<ISqlFormatterElem>
Public Function getSqlEnvs() As IObjArrayRef
    Set getSqlEnvs = sqlEnvCache
End Function

' ==================================================
' 获取、生成辅助对象: 各类型 sql 的组成部分的解析器
' ==================================================
' 获取、生成 select sql 的组成部分解析器
Private Function getSelectAnalyzer() As SqlFormatterSelectSubEAnalyzer
    ' 检查: 如果属性对象 `selectAnalyzer` 是空，则进行创建
    If selectAnalyzer Is Nothing Then
        Set selectAnalyzer = New SqlFormatterSelectSubEAnalyzer
        selectAnalyzer.Init Me
    End If

    Set getSelectAnalyzer = selectAnalyzer
End Function

' 获取、生成 update sql 的组成部分解析器
Private Function getUpdateAnalyzer() As SqlFormatterUpdateSubEAnalyzer
    ' 检查: 如果属性对象 `updateAnalyzer` 是空，则进行创建
    If updateAnalyzer Is Nothing Then
        Set updateAnalyzer = New SqlFormatterUpdateSubEAnalyzer
        updateAnalyzer.Init Me
    End If

    Set getUpdateAnalyzer = updateAnalyzer
End Function

' 获取、生成 delete sql 的组成部分解析器
Private Function getDeleteAnalyzer() As SqlFormatterDeleteSubEAnalyzer
    ' 检查: 如果属性对象 `deleteAnalyzer` 是空，则进行创建
    If deleteAnalyzer Is Nothing Then
        Set deleteAnalyzer = New SqlFormatterDeleteSubEAnalyzer
        deleteAnalyzer.Init Me
    End If

    Set getDeleteAnalyzer = deleteAnalyzer
End Function

' 获取、生成 insert sql 的组成部分解析器
Private Function getInsertAnalyzer() As SqlFormatterInsertSubEAnalyzer
    ' 检查: 如果属性对象 `insertAnalyzer` 是空，则进行创建
    If insertAnalyzer Is Nothing Then
        Set insertAnalyzer = New SqlFormatterInsertSubEAnalyzer
        insertAnalyzer.Init Me
    End If

    Set getInsertAnalyzer = insertAnalyzer
End Function

' ======================================================================
' 辅助方法
' ======================================================================

' 清除格式化器中的缓存
Private Sub clearCacheData()
    ' 1. 清除上一次解析时生成的 sql: 设置 `multiSqlMeanTokenCache = null`
    Set multiSqlMeanTokenCache = Nothing
    ' 2. 清除上一次解析时生成的所有作用域的缓存
    Set sqlEnvCache = Nothing
End Sub

' 对 sql 字符串进行分词
'
' @param sql sql 字符串
Private Sub extractorSqlToken(ByRef sql As String)
    ' 1. 创建 sql 解析器
    If tokenExtractor Is Nothing Then
        Set tokenExtractor = New SqlTokenExtractor
        ' 这里不需要保留全部的元素
        tokenExtractor.Init
        ' 解析时需要保留注释
        tokenExtractor.setSqlCommentIsMeanToken True
    End If

    ' 2. 解析 sql，并按照 [;] 分隔
    ' 这里不需要保留 [;]
    tokenExtractor.analyzeMultiSql sql, False

    ' 3. 获取解析后的 token
    Set multiSqlMeanTokenCache = tokenExtractor.multiMeaningfulTokens
End Sub

' 获取Not开头的条件运算符
'
' @param tokenScanner 扫描器
' @return Not开头的条件运算符
'         如果不是 Not 开头的[条件运算符]，将会返回 null
Public Function getNotConditionOp(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. not 元素 `SqlToken notToken`
    Dim notToken As SqlToken
    ' 2. token `SqlToken token`
    Dim token As SqlToken
    ' 3. 结果对象 `SqlFormatterTokenGroup result`
    Dim result As SqlFormatterTokenGroup

    ' ==================================================
    ' 2. 标记扫描器
    ' ==================================================
    tokenScanner.mark

    ' ==================================================
    ' 3. 处理 not
    ' ==================================================
    ' 1. 从扫描器中弹出一个 token，保存到 `notToken`
    Set notToken = tokenScanner.getNext
    ' 2. 检查: 如果扫描器已经用完
    If Not tokenScanner.hasNext Then
        ' 2.1. 清除扫描器标记
        tokenScanner.clearMark
        ' 2.2. 则退出处理，并返回 `notToken`
        Set getNotConditionOp = notToken
        Exit Function
    End If

    ' ==================================================
    ' 4. 匹配条件运算符
    ' ==================================================
    ' 1. 从扫描器中获取一个 `token`
    Set token = tokenScanner.peek
    ' 2. 检查: 如果 token 不是条件运算符
    If Not SqlHelper.tokenTypeIsConditionOperator(token.tokenType) Then
        ' 2.1. 恢复扫描器的标记位置，并清除标记: `resetAndClearMark`
        tokenScanner.resetAndClearMark
        ' 2.2. 退出处理，并返回 null
        ' return null
        Exit Function
    End If
    ' 3. 扫描器向前推进
    tokenScanner.toNext

    ' ==================================================
    ' 5. 生成结果
    ' ==================================================
    ' 1. 创建结果对象: `result = new SqlFormatterTokenGroup()`
    Set result = New SqlFormatterTokenGroup
    ' 2. 保存 `notToken` 到 `result`
    result.ISqlFormatterElem_addComponent notToken
    ' 3. 保存 `token` 到 `result`
    result.ISqlFormatterElem_addComponent token
    ' 4. 设置结果的类型为 `token.tokenType`
    result.setType token.tokenType
    ' 5. 清除扫描器标记
    tokenScanner.clearMark

    ' ==================================================
    ' 6. 退出处理，返回 `result`
    ' ==================================================
    Set getNotConditionOp = result
End Function

' 为一个token吸收一个同行注释并合并为TokenGroup
'
' @param t 原始元素
' @param scanner 扫描器
public Function consumeShareLineCommentToTokenGroup(ByRef t As ISqlFormatterElem, ByRef scanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' 1. 检查: 如果扫描器已经用完，则退出处理，并返回 `t`
    If Not scanner.hasNext Then
        ' return t
        Set consumeShareLineCommentToTokenGroup = t
        Exit Function
    End If

    ' 2. 从扫描器中获取一个 `SqlToken token`
    Dim token As SqlToken
    Set token = scanner.peek

    ' 3. 检查: 如果 token 不是同行注释，则退出处理，并返回 `t`
    If token.ISqlFormatterElem_isShareLineComment = False Then
        ' return t
        Set consumeShareLineCommentToTokenGroup = t
        Exit Function
    End If

    ' 4. 创建一个 token 组合
    ' 4. 创建 SqlFormatterTokenGroup
    Dim result As SqlFormatterTokenGroup
    ' 4.1. 检查: 如果 t 的类型是 SqlFormatterTokenGroup，则设置 `result = t`
    If TypeOf t Is SqlFormatterTokenGroup Then
        Set result = t

    ' 4.2. 否则
    Else
        ' 4.2.1. 创建一个 token 组合: `SqlFormatterTokenGroup result = new SqlFormatterTokenGroup()`
        Set result = New SqlFormatterTokenGroup
        ' 4.2.2. 设置组合的类型为 `t.getType` :`setType`
        result.setType t.getType
    End If

    ' 5. 向前推进扫描器
    scanner.toNext

    ' 6. 将 `t` 添加到 `result`
    result.ISqlFormatterElem_addComponent t
    ' 7. 将 `token` 添加到 `result`
    result.ISqlFormatterElem_addComponent token
    ' 8. 退出处理，并返回 `result`
    Set consumeShareLineCommentToTokenGroup = result
End Function

' 将左括号栈回退到扫描器内
'
' @param tokenScanner 扫描器
' @param stack 栈对象 (ObjStack<SqlToken>)
Public Sub putBackStackToScanner( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef stack As ObjStack)
    If stack Is Nothing Then
        Exit Sub
    End If

    tokenScanner.back stack.Count
End Sub

' 解析开头连续出现的左括号
'
' @param tokenScanner 扫描器
' @return 左括号栈 (ObjStack<SqlToken>)
Public Function parseStartLeftParenthesis(ByRef tokenScanner As ObjArrayRefScanner) As ObjStack
    ' ==================================================
    ' 1. 处理第一个元素
    ' ==================================================
    ' 1. 从 `tokenScanner` 中获取一个 `token`，并获取类型到 `tokenType`
    Dim token As SqlToken
    Set token = tokenScanner.peek
    Dim tokenType As SqlTokenType
    tokenType = token.tokenType

    ' 2. 检查第一个 token 的类型
    ' 2.1. 检查，如果 `token.tokenType == SqlTokenType.LEFT_PARENTHESIS`，开始处理连续出现的 `(`
    If tokenType = SqlTokenType.LEFT_PARENTHESIS Then
        ' noop

    ' 2.2. 否则，如果 `token.tokenType` 是注释，开始处理连续出现的 `(`
    ElseIf SqlHelper.isCommentToken(tokenType) Then
        ' noop

    ' 2.3. 否则，立刻结束，并返回 null
    Else
        ' return null
        Exit Function
    End If

    ' 3. 从池中获取一个对象类型的栈作为左括号栈 `ObjStack<SqlToken> result`
    '    这里直接使用 parseStartLeftParenthesis
    Set parseStartLeftParenthesis = UtilsPool.getObjStack

    ' 4. 将当前 token 添加到栈 `result.push(token)`
    parseStartLeftParenthesis.push token

    ' 5. 向前推进 `tokenScanner`
    tokenScanner.toNext

    ' ==================================================
    ' 2. 遍历, 继续处理后面可能会连续出现的 `(`
    ' ==================================================
    ' 1. 如果扫描器用完了，则退出遍历
    While tokenScanner.hasNext
        ' 2. 从 `tokenScanner` 中获取一个 `token`，并获取类型到 `tokenType`
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' 3. 检查 token 的类型
        ' 3.1. 检查，如果 `tokenType == SqlTokenType.LEFT_PARENTHESIS`，则继续
        If tokenType = SqlTokenType.LEFT_PARENTHESIS Then
            ' noop

        ' 3.2. 否则，如果 `tokenType` 是注释，则继续
        ElseIf SqlHelper.isCommentToken(tokenType) Then
            ' noop

        ' 3.3. 否则，则退出遍历
        Else
            GoTo whileBreak
        End If

        ' 4. 将 `token` 添加到作用域 `result.push(token)`
        parseStartLeftParenthesis.push token

        ' 5. 向前推进 `tokenScanner`
        tokenScanner.toNext

        ' 6. 回到 1 继续遍历
    Wend
whileBreak:

    ' ==================================================
    ' 3. 返回 `result`
    ' ==================================================
    ' return result;
End Function

' 解析开头连续出现的右括号
'
' @param tokenScanner 扫描器
' @return 包含右括号的容器作用域
Public Function parseStartRightParenthesis(ByRef tokenScanner As ObjArrayRefScanner) As SqlFormatterContainerEnv
    ' ==================================================
    ' 1. 参数检查
    ' ==================================================
    ' 1. 如果扫描器已经用完，则立刻结束，并返回 null
    if Not tokenScanner.hasNext Then
        ' return null
        Exit Function
    End If

    ' ==================================================
    ' 2. 快速结束
    ' ==================================================
    ' 1. 检查: 如果 [扫描器到下一个最近的右括号的偏移量](#获取扫描器到下一个最近的右括号的偏移量) 是 0，则退出处理，并返回 null
    If SqlFormatterHelper.getNextRightParenthesisOffset(tokenScanner) = 0 Then
        ' return null
        Exit Function
    End If

    ' ==================================================
    ' 3. 处理第一个元素
    ' ==================================================
    ' 1. 从 `tokenScanner` 中获取一个 `token`，并获取类型到 `tokenType`
    Dim token As SqlToken
    Set token = tokenScanner.peek

    Dim tokenType As SqlTokenType
    tokenType = token.tokenType

    ' 2. 检查第一个 token 的类型
    ' 2.1. 检查，如果 `tokenType == SqlTokenType.RIGHT_PARENTHESIS`，开始处理连续出现的 `)`
    If tokenType = SqlTokenType.RIGHT_PARENTHESIS Then
        ' noop

    ' 2.2. 否则，如果是注释，开始处理连续出现的 `)`
    ElseIf SqlHelper.isCommentToken(tokenType) Then
        ' noop

    ' 2.3. 否则，立刻结束，并返回 null
    Else
        ' return null
        Exit Function
    End If

    ' 3. 创建一个包装作用域 `SqlFormatterContainerEnv result`
    '    这里直接使用 parseStartRightParenthesis
    Set parseStartRightParenthesis = New SqlFormatterContainerEnv

    ' 4. 将当前 token 添加到作用域
    parseStartRightParenthesis.ISqlFormatterElem_addComponent token

    ' 5. 向前推进 `tokenScanner`
    tokenScanner.toNext

    ' ==================================================
    ' 4. 遍历, 继续处理后面可能会连续出现的 `)`
    ' ==================================================
    ' 1. 如果扫描器用完了，则退出遍历
    While tokenScanner.hasNext
        ' 2. 从 `tokenScanner` 中获取一个 `token`，并获取类型到 `tokenType`
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' 3. 检查 token 的类型
        ' 3.1. 检查，如果 `tokenType == SqlTokenType.RIGHT_PARENTHESIS`，则继续
        If tokenType = SqlTokenType.RIGHT_PARENTHESIS Then
            ' noop

        ' 3.2. 否则，如果 `tokenType` 是注释，则继续
        ElseIf SqlHelper.isCommentToken(tokenType) Then
            ' noop

        ' 3.3. 否则，退出遍历
        Else
            GoTo whileBreak
        End If

        ' 4. 将 `token` 添加到作用域
        parseStartRightParenthesis.ISqlFormatterElem_addComponent token

        ' 5. 向前推进 `tokenScanner`
        tokenScanner.toNext

        ' 6. 回到 1 继续遍历
    Wend
whileBreak:

    ' ==================================================
    ' 5. 返回 `result`
    ' ==================================================
    ' return result;
End Function

' 合并两个with作用域
'
' @param src 保存 withEnv 的作用域: SqlFormatterMultiSameEnvGroup 类型的作用域或者普通的 withEnv
' @param desc 被合并的 withEnv
' @return 合并后的容器作用域
Public Function saveWithEnvGroup( _
    ByRef src As ISqlFormatterElem, _
    ByRef desc As ISqlFormatterElem) As ISqlFormatterElem
    ' ==================================================
    ' 1. 参数检查
    ' ==================================================
    ' 1. 检查: 如果 `src == null`，则返回 `desc`
    If src Is Nothing Then
        Set saveWithEnvGroup = desc
        Exit Function
    End If

    ' 2. 否则，检查: 如果 `desc == null`，则返回 `src`
    If desc Is Nothing Then
        Set saveWithEnvGroup = src
        Exit Function
    End If

    ' ==================================================
    ' 2. 处理 `src`
    ' ==================================================
    ' 1. 声明结果作用域 `SqlFormatterContainerEnv result`
    Dim result As SqlFormatterContainerEnv

    ' 2. 检查: 如果 `src` 类型不是 `SqlFormatterContainerEnv`
    If Not Typeof src Is SqlFormatterContainerEnv Then
        ' 2.1. 新建 `result`
        Set result = New SqlFormatterContainerEnv
        ' 2.2. 保存 `src` 到 `result`
        result.ISqlFormatterElem_addComponent src

    ' 3. 否则
    Else
        ' 3.1. 保证 src 作用域的可添加性，结果保存到 `result`
        Set result = ensureComponentAdditivityEnv(src)
    End If

    ' ==================================================
    ' 3. 处理 `desc`
    ' ==================================================
    ' 1. 保存 `desc` 到 `result`
    result.ISqlFormatterElem_addComponent desc

    ' ==================================================
    ' 4. 处理类型
    ' ==================================================
    ' 1. 设置 `result` 的类型为 `SqlTokenType.KEY_WITH`
    result.setType SqlTokenType.KEY_WITH

    ' ==================================================
    ' 5. 退出处理，返回 `result`
    ' ==================================================
    Set saveWithEnvGroup = result
End Function

' 合并with和其他类型作用域 (合并成一个容器作用域)
'
' @param withEnv with 作用域
' @param mainEnv 其他需要合并的作用域
' @return 合并后的容器作用域
Public Function mergeWithEnv( _
    ByRef withEnv As ISqlFormatterElem, _
    ByRef mainEnv As ISqlFormatterElem) As SqlFormatterContainerEnv

    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `SqlFormatterContainerEnv result`
    '    直接使用 mergeWithEnv

    ' ==================================================
    ' 2. 尝试以 `mainEnv` 为核心保存
    ' ==================================================
    ' 1. 检查: 如果可以从左侧添加元素 `canAddComponentToFirst() == true`，则继续
    If mainEnv.canAddComponentToFirst Then
        ' 2. 将 `withEnv` 添加到 `mainEnv` 的前面: `addComponentToFirst`
        mainEnv.addComponentToFirst withEnv
        ' 3. 返回 `mainEnv`
        Set mergeWithEnv = mainEnv
        Exit Function
    End If

    ' ==================================================
    ' 3. 尝试以 `withEnv` 为核心保存
    ' ==================================================
    ' 1. 检查: 如果 `withEnv` 的类型是 `SqlFormatterContainerEnv`，则继续
    If Typeof withEnv Is SqlFormatterContainerEnv Then
        ' 2. 检查: 如果 `withEnv` 不存在边界括号: `hasBoundaryParenthesis() == false`，则继续
        If withEnv.hasBoundaryParenthesis = False Then
            ' 3. 强转类型 `result = withEnv`
            Set mergeWithEnv = withEnv
            ' 4. 将 `mainEnv` 添加到 `result`
            mergeWithEnv.ISqlFormatterElem_addComponent mainEnv
            ' 5. 设置 `result` 的类型为 `main` 的类型
            mergeWithEnv.setType mainEnv.getType
            ' 6. 退出处理，并返回 `result`
            ' return result
            Exit Function
        End if
    End If

    ' ==================================================
    ' 4. 创建新的容器作用域来保存
    ' ==================================================
    ' 1. 新建 `result`
    Set mergeWithEnv = New SqlFormatterContainerEnv
    ' 2. 将 `withEnv` 添加到 `result`
    mergeWithEnv.ISqlFormatterElem_addComponent withEnv
    ' 3. 将 `mainEnv` 添加到 `result`
    mergeWithEnv.ISqlFormatterElem_addComponent mainEnv
    ' 4. 设置 `result` 的类型为 `main` 的类型
    mergeWithEnv.setType mainEnv.getType
    ' 5. 退出处理，并返回 `result`
    ' return result
End Function

' 将两个作用域保存到容器作用域 (合并时，将会利用参数中已有的容器作用域)
'
' @param e1 需要合并的作用域
' @param e2
' @return 合并后的容器作用域
Public Function containEnvs( _
    ByRef e1 As ISqlFormatterElem, _
    ByRef e2 As ISqlFormatterElem) As SqlFormatterContainerEnv
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `SqlFormatterContainerEnv result`
    '    这里直接使用 containEnvs

    ' ==================================================
    ' 2. 以 e1 为核心合并
    ' ==================================================
    ' 1. 检查: 如果 e1 的类型是 `SqlFormatterContainerEnv`，则继续
    If Typeof e1 Is SqlFormatterContainerEnv Then
        ' 2. 检查: 如果 `e1` 不存在边界括号
        If Not e1.hasBoundaryParenthesis Then
            ' 2.1. 转换类型 `result = e1`
            Set containEnvs = e1
            ' 2.2 将 `e2` 添加到 e1 的末尾
            containEnvs.ISqlFormatterElem_addComponent e2
            ' 2.3 清除 `e1` 的类型
            containEnvs.setType SqlTokenType.NO_MEAN
            ' 2.4 立即结束，并返回 `result`
            ' return result
            Exit Function
        End If
    End If

    ' ==================================================
    ' 3. 以 e2 为核心合并
    ' ==================================================
    ' 1. 检查: 如果 e2 的类型是 `SqlFormatterContainerEnv`，则继续
    If Typeof e2 Is SqlFormatterContainerEnv Then
        ' 2. 检查: 如果 `e1` 可以在左侧添加元素，则继续
        If e2.canAddComponentToFirst Then
            ' 2.1 转换类型 `result = e2`
            Set containEnvs = e2
            ' 2.2 将 `e1` 作为组成元素添加到 result 的开头
            containEnvs.ISqlFormatterElem_addComponentToFirst e1
            ' 2.3 清除 `result` 的类型
            containEnvs.setType SqlTokenType.NO_MEAN
            ' 2.4 立即结束，并返回 `result`
            ' return result
            Exit Function
        End If
    End If

    ' ==================================================
    ' 4. 创建一个新的容器作用域，并保存作用域参数
    ' ==================================================
    ' 1. 创建容器作用域 `result`
    Set containEnvs = New SqlFormatterContainerEnv
    ' 2. 将 `e1` 添加到 `result`
    containEnvs.ISqlFormatterElem_addComponent e1
    ' 3. 将 `e2` 添加到 `result`
    containEnvs.ISqlFormatterElem_addComponent e2

    ' ==================================================
    ' 5. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 生成union关键字
'
' @param tokenScanner 扫描器
' @return union关键字元素
Public Function parseUnionKey(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 弹出 `unionToken`
    ' ==================================================
    Dim unionToken As SqlToken
    Set unionToken = tokenScanner.getNext

    ' ==================================================
    ' 2. 检查，如果扫描器已经用完，则退出，并返回 `unionToken`
    ' ==================================================
    If Not tokenScanner.hasNext Then
        Set parseUnionKey = unionToken
        Exit Function
    End If

    ' ==================================================
    ' 3. 尝试获取 `ALL` 关键字
    ' ==================================================
    ' 1. 从扫描器中获取下一个 `allToken`
    Dim allToken As SqlToken
    Set allToken = tokenScanner.peek

    ' 2. 检查，如果 `allToken` 类型不是 `SqlTokenType.KEY_ALL`，则停止，并返回 `unionToken`
    If allToken.tokenType <> SqlTokenType.KEY_ALL Then
        Set parseUnionKey = unionToken
        Exit Function
    End If
    ' 3. 否则，扫描器向前推进
    tokenScanner.toNext

    ' ==================================================
    ' 4. 生成 UNION ALL
    ' ==================================================
    ' 1. 创建 `SqlFormatterTokenGroup keyGroup = new SqlFormatterTokenGroup`
    Dim keyGroup As SqlFormatterTokenGroup
    Set keyGroup = New SqlFormatterTokenGroup
    ' 2. 保存 `unionToken` 到 `keyGroup`
    keyGroup.ISqlFormatterElem_addComponent unionToken
    ' 3. 保存 `allToken` 到 `keyGroup`
    keyGroup.ISqlFormatterElem_addComponent allToken
    ' 4. 结束，并返回 `keyGroup`
    Set parseUnionKey = keyGroup
End Function

' 吸收union关键字到作用域
'
' @param tokenScanner 扫描器
' @param env 需要吸收 union 关键字的作用域
' @return 吸收union关键字后的作用域
Public Function mergeKeyUnion( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef env As ISqlFormatterElem) As ISqlFormatterElem
    ' ==================================================
    ' 1. 生成 union 关键字
    ' ==================================================
    ' 1. 生成union关键字，并保存到 `ISqlFormatterElem unionKey`
    Dim unionKey As ISqlFormatterElem
    Set unionKey = parseUnionKey(tokenScanner)
    ' 2. 为 unionKey 吸收一个同行注释并合并为TokenGroup，结果保存到 `unionKey`
    Set unionKey = consumeShareLineCommentToTokenGroup(unionKey, tokenScanner)

    ' ==================================================
    ' 2. 初始化
    ' ==================================================
    ' 1. 声明结果作用域 `SqlFormatterContainerEnv result`
    Dim result As SqlFormatterContainerEnv
    ' 2. 检查: 如果类型不是 `SqlFormatterContainerEnv`
    If Not TypeOf env Is SqlFormatterContainerEnv Then
        ' 2.1. 创建结果作用域 `result`
        Set result = New SqlFormatterContainerEnv
        ' 2.2. 将 `env` 保存到 `result`
        result.ISqlFormatterElem_addComponent env

    ' 3. 否则，检查: 如果作用域存在边界括号，则创建结果作用域 `result`
    ElseIf env.hasBoundaryParenthesis Then
        ' 3.1. 创建结果作用域 `result`
        Set result = New SqlFormatterContainerEnv
        ' 3.2. 将 `env` 保存到 `result`
        result.ISqlFormatterElem_addComponent env

    ' 4. 否则，转换类型，设置 `result = env`
    Else
        Set result = env
    End If

    ' ==================================================
    ' 3. 将 `unionKey` 保存到 `result`
    ' ==================================================
    result.ISqlFormatterElem_addComponent unionKey

    ' ==================================================
    ' 4. 设置 `result` 的类型为: `SqlTokenType.KEY_UNION`
    ' ==================================================
    result.setType SqlTokenType.KEY_UNION

    ' ==================================================
    ' 5. 返回 `result`
    ' ==================================================
    Set mergeKeyUnion = result
End Function

' 吸收一个token到作用域右侧
'
' @param env 用来吸收 token 的作用域
' @param token 需要吸收的 token
' @return 吸收 token 后的作用域
Public Function consumeTokenToEnvRight(ByRef env As ISqlFormatterElem, ByRef token As SqlToken) As ISqlFormatterElem
    ' 1. 保证作用域的可添加性，结果保存到 `ISqlFormatterElem result`
    Set consumeTokenToEnvRight = ensureComponentAdditivityEnv(env)

    ' 2. 将 `token` 作为普通元素保存到 `result` 的左侧
    consumeTokenToEnvRight.addComponent token

    ' 3. 结束，返回 `result`
    ' return result
End Function

' 吸收一个同行注释到作用域右侧
'
' @param env 原始作用域对象
' @param tokenScanner 扫描器
Public Function consumeOneShareLineCommentToEnvRight( _
    ByRef env As ISqlFormatterElem, ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' 1. 检查: 如果扫描器用完，则退出处理，并返回 `env`
    If Not tokenScanner.hasNext Then
        ' return env
        Set consumeOneShareLineCommentToEnvRight = env
        Exit Function
    End If

    ' 2. 从 `tokenScanner` 中获取一个 `SqlToken token`
    Dim token As SqlToken
    Set token = tokenScanner.peek

    ' 3. 检查: 如果 token 不是同行注释，则退出处理，并返回 `env`: `ISqlFormatterElem_isShareLineComment() == false`
    If token.ISqlFormatterElem_isShareLineComment = False Then
        ' return env
        Set consumeOneShareLineCommentToEnvRight = env
        Exit Function
    End If

    ' 4. 声明变量
    ' 4.1. 结果对象 `ISqlFormatterElem result`
    '      这里直接使用 consumeOneShareLineCommentToEnvRight

    ' 5. 设置结果对象 `result = env`
    Set consumeOneShareLineCommentToEnvRight = env

    ' 6. 检查: 如果 `result` 存在边界括号: `hasBoundaryParenthesis()`
    If consumeOneShareLineCommentToEnvRight.hasBoundaryParenthesis Then
        ' 6.1. 将 `result` 包装为容器作用域，并返回: `SqlFormatterHelper.wrapperEnvToContainerEnv`
        Set consumeOneShareLineCommentToEnvRight = SqlFormatterHelper.wrapperEnvToContainerEnv(consumeOneShareLineCommentToEnvRight)
    End If

    ' 7. 将 `token` 保存到 `result`: `addComponent`
    consumeOneShareLineCommentToEnvRight.addComponent token

    ' 8. 向前推进扫描器
    tokenScanner.toNext

    ' 9. 退出处理，并返回 `result`
    ' return result
End Function

' 吸收多个元素到作用域右侧
'
' @param env 原始作用域对象
' @param tokenScanner 扫描器
' @param count 需要吸收的元素数量
Public Function consumeComponentToEnvRight( _
    ByRef env As ISqlFormatterElem, _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef count As Integer) As ISqlFormatterElem
    ' 1. 声明变量
    ' 1.1. 遍历索引 `int i`
    Dim i As Integer
    ' 1.2. 结果对象 `ISqlFormatterElem result`
    '      这里直接使用 consumeComponentToEnvRight

    ' 2. 保存作用域到结果对象 `result = env`
    Set consumeComponentToEnvRight = env

    ' 3. 检查: 如果 `env` 存在边界括号: `hasBoundaryParenthesis() == true`
    If consumeComponentToEnvRight.hasBoundaryParenthesis Then
        ' 3.1. 将 `result` 包装为容器作用域，并保存到 `result`: `SqlFormatterHelper.wrapperEnvToContainerEnv`
        Set consumeComponentToEnvRight = SqlFormatterHelper.wrapperEnvToContainerEnv(consumeComponentToEnvRight)
    End If

    ' 4. 从 `i = 1` 开始到 `count` 遍历
    For i = 1 To count
        ' 4.1. 从扫描器中弹出一个 token，并保存到 `result`
        consumeComponentToEnvRight.addComponent tokenScanner.getNext
    Next

    ' 5. 退出处理，返回 `result`
    ' return result
End Function

' 保证作用域的可添加性
'
' @param env 作用域对象
' @return 可以添加元素的作用域
Public Function ensureComponentAdditivityEnv(ByRef env As ISqlFormatterElem) As ISqlFormatterElem
    ' 1. 检查: 如果 `env` 不存在边界括号
    If Not env.hasBoundaryParenthesis Then
        ' 1. 结束，返回 `env`
        ' return env
        Set ensureComponentAdditivityEnv = env
        Exit Function
    End If

    ' 2. 否则 (`env` 存在边界括号)，将一个作用域包装为容器作用域，并返回
    Set ensureComponentAdditivityEnv = SqlFormatterHelper.wrapperEnvToContainerEnv(env)
End Function

' 吸收左括号栈栈顶连续出现的注释到作用域左侧
'
' @param env 作用域
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @return 吸收左括号栈后的作用域
Public Function consumeLPStackTopCommentToEnvLeft( _
    ByRef env As ISqlFormatterElem, _
    ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象
    ' ISqlFormatterElem result
    '    这里直接使用 consumeLPStackTopCommentToEnvLeft

    ' ==================================================
    ' 2. 初始化 `result`
    ' ==================================================
    ' 1. 检查: 如果 `env` 可以在左侧添加元素，则设置 `result = env`
    If env.canAddComponentToFirst Then
        Set consumeLPStackTopCommentToEnvLeft = env

    ' 2. 否则，将 `env` 包装为容器作用域，保存到 `result`
    Else
        Set consumeLPStackTopCommentToEnvLeft = SqlFormatterHelper.wrapperEnvToContainerEnv(env)
    End If

    ' ==================================================
    ' 3. 遍历 `leftParenthesisStack`
    ' ==================================================
    Dim token As SqlToken
    ' 1. 如果 `leftParenthesisStack.Count == 0`，则停止遍历
    while leftParenthesisStack.Count <> 0
        ' 2. 获取左括号栈的栈顶 `token`
        Set token = leftParenthesisStack.peek

        ' 3. 检查，如果 `token` 不是注释，则退出遍历
        If token.ISqlFormatterElem_isComment = False Then
            GoTo whileBreak
        End If

        ' 4. 否则 (是注释)
        ' 4.1. 弹出栈顶
        leftParenthesisStack.pop
        ' 4.2. 将 `token` 作为普通元素保存到 `result` 的左侧
        consumeLPStackTopCommentToEnvLeft.addComponentToFirst token
        ' 4.3. 进入下一次遍历
    Wend
whileBreak:

    ' ==================================================
    ' 4. 返回 `result`
    ' ==================================================
    ' return result
End Function

' 吸收左括号栈栈顶连续出现的单行注释到作用域左侧
'
' @param env 作用域
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @return 吸收左括号栈后的作用域
Public Function consumeLPStackTopSingleLineCommentToEnvLeft( _
    ByRef env As ISqlFormatterElem, _
    ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 consumeLPStackTopSingleLineCommentToEnvLeft
    ' 2. token `SqlToken token`
    Dim token As SqlToken

    ' ==================================================
    ' 2. 检查第一个 token
    ' ==================================================
    ' 1. 从左括号栈中获取一个 token
    Set token = leftParenthesisStack.peek

    ' 2. 检查: 如果 token 不是单行注释，则退出处理，并返回 `env`: `token.ISqlFormatterElem_isSingleLineComment() == false`
    If token.ISqlFormatterElem_isSingleLineComment = False Then
        ' return env
        Set consumeLPStackTopSingleLineCommentToEnvLeft = env
        Exit Function
    End If

    ' ==================================================
    ' 3. 初始化 `result`
    ' ==================================================
    ' 1. 检查: 如果 `env` 可以在左侧添加元素，则设置 `result = env`: `canAddComponentToFirst`
    If env.canAddComponentToFirst Then
        Set consumeLPStackTopSingleLineCommentToEnvLeft = env

    ' 2. 否则，将 `env` 包装为容器作用域，保存到 `result`
    Else
        Set consumeLPStackTopSingleLineCommentToEnvLeft = SqlFormatterHelper.wrapperEnvToContainerEnv(env)
    End If

    ' ==================================================
    ' 4. 保存第一个 token
    ' ==================================================
    ' 1. 将 `token` 保存到 `result` 的左侧: `addComponentToFirst()`
    consumeLPStackTopSingleLineCommentToEnvLeft.addComponentToFirst token

    ' 2. 弹出栈顶
    leftParenthesisStack.pop

    ' ==================================================
    ' 5. 遍历 `leftParenthesisStack`，吸收剩余的单行注释
    ' ==================================================
    ' 1. 如果 `leftParenthesisStack.Count == 0`，则停止遍历
    While leftParenthesisStack.Count > 0
        ' 2. 获取左括号栈的栈顶 `token`
        Set token = leftParenthesisStack.peek

        ' 3. 检查，如果 `token` 不是单行注释，则退出遍历: `ISqlFormatterElem_isSingleLineComment() == false`
        If token.ISqlFormatterElem_isSingleLineComment = False Then
            GoTo whileBreak
        End If

        ' 4. 否则 (是单行注释)
        ' 4.1. 弹出栈顶
        leftParenthesisStack.pop
        ' 4.2. 将 `token` 作为普通元素保存到 `result` 的左侧: `addComponentToFirst()`
        consumeLPStackTopSingleLineCommentToEnvLeft.addComponentToFirst token
        ' 4.3. 进入下一次遍历
    Wend
whileBreak:

    ' ==================================================
    ' 6. 返回 `result`
    ' ==================================================
    ' return result
End Function

' 吸收右侧连续出现的注释到作用域
'
' @param tokenScanner 扫描器
' @param env 作用域
' @return 吸收注释后的作用域
Public Function consumeCommentToEnvRight( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef env As ISqlFormatterElem) As ISqlFormatterElem
    ' ==================================================
    ' 1. 快速结束
    ' ==================================================
    ' 1. 检查: 如果扫描器用完了，则退出，并返回 `env`
    If Not tokenScanner.hasNext Then
        ' return env
        Set consumeCommentToEnvRight = env
        Exit Function
    End If

    ' ==================================================
    ' 2. 检查第一个元素
    ' ==================================================
    ' 1. 声明变量 token `SqlToken token`
    Dim token As SqlToken
    ' 2. 从扫描器中获取一个 token
    Set token = tokenScanner.peek
    ' 3. 检查: 如果 token 不是注释，则退出处理，并返回 `env`: `ISqlFormatterElem_isComment() == false`
    If token.ISqlFormatterElem_isComment = False Then
        ' return env
        Set consumeCommentToEnvRight = env
        Exit Function
    End If

    ' ==================================================
    ' 3. 创建结果对象
    ' ==================================================
    ' 1. 声明结果对象 `ISqlFormatterElem result`
    '    这里直接使用

    ' 2. 检查: 如果 `env` 不存在边界括号，则设置 `result = env`: `hasBoundaryParenthesis() == false`
    If env.hasBoundaryParenthesis = False Then
        Set consumeCommentToEnvRight = env

    ' 3. 否则 (`env` 存在边界括号)，将 `env` 包装为容器作用域，并保存到 `result`
    Else
        Set consumeCommentToEnvRight = SqlFormatterHelper.wrapperEnvToContainerEnv(env)
    End If
    ' 4. 将 `token` 保存到 `result`
    consumeCommentToEnvRight.addComponent token
    ' 5. 向前推进扫描器
    tokenScanner.toNext

    ' ==================================================
    ' 4. 继续遍历处理右侧的注释
    ' ==================================================
    ' 1. 如果: 扫描器用完了，则退出遍历
    While tokenScanner.hasNext
        ' 2. 从 `tokenScanner` 中获取一个 `token`
        Set token = tokenScanner.peek
        ' 3. 检查: 如果 token 是注释: `ISqlFormatterElem_isComment() == true`
        If token.ISqlFormatterElem_isComment = True Then
            ' 3.1. 将 `token` 保存到 `result`
            consumeCommentToEnvRight.addComponent token
            ' 3.2. 向前推进扫描器
            tokenScanner.toNext
            ' 3.3. 进入下一次遍历

        ' 4. 否则（不是注释），则退出遍历
        Else
            GoTo whileBreak
        End If
    Wend
whileBreak:

    ' ==================================================
    ' 5. 返回 `result`
    ' ==================================================
    ' return result
End Function

' 作用域括号配对
'
' @param env 原始作用域
' @param tokenScanner 扫描器
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @return 吸收括号后的作用域
Public Function matchEnvBoundaryParenthesis( _
    ByRef env As ISqlFormatterElem, _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem

    ' ==================================================
    ' 1. 设置 `ISqlFormatterElem result = env`
    ' ==================================================
    ' ISqlFormatterElem result = env
    ' 这里直接使用
    Set matchEnvBoundaryParenthesis = env

    Dim token As SqlToken

    ' ==================================================
    ' 2. 检查: 如果不存在左括号栈
    ' ==================================================
    If UtilsStack.isEmptyObjStack(leftParenthesisStack) Then
        ' 1. 尝试: 吸收一个同行注释到 result 右侧，结果保存到 `result`
        Set matchEnvBoundaryParenthesis = consumeOneShareLineCommentToEnvRight(matchEnvBoundaryParenthesis, tokenScanner)

        ' 2. 退出处理，并返回 `result`
        Exit Function
    End If

    ' ==================================================
    ' 3. 声明变量
    ' ==================================================
    ' 1. 偏移量 `int offset`
    Dim offset As Integer
    ' 2. 遍历索引 `int i`
    Dim i As Integer

    ' ==================================================
    ' 4. 遍历处理 `)`
    ' ==================================================
    ' 1. 如果: 扫描器用完了，或者左括号栈用完，则退出遍历
    While leftParenthesisStack.Count <> 0 And tokenScanner.hasNext
        ' 2. 执行: 获取扫描器到下一个最近的右括号的偏移量，结果保存到 `offset`
        offset = SqlFormatterHelper.getNextRightParenthesisOffset(tokenScanner)

        ' 3. 检查: 如果 `offset == 0`，则退出遍历
        If offset = 0 Then
            GoTo whileBreak
        End If

        ' 4. 否则，进行括号配对 (现在发现了一个右括号)
        ' 4.1. 获取左括号栈的栈顶 `token`
        Set token = leftParenthesisStack.peek

        ' 4.2. 检查: 如果 `token` 是注释
        If token.ISqlFormatterElem_isComment Then
            ' 4.2.1. 吸收左括号栈栈顶连续出现的注释到作用域左侧，结果保存到 `result`
            Set matchEnvBoundaryParenthesis = consumeLPStackTopCommentToEnvLeft( _
                matchEnvBoundaryParenthesis, leftParenthesisStack)
            ' 4.2.2. 检查，如果左括号栈用完 `leftParenthesisStack.Count == 0`，则退出遍历
            If leftParenthesisStack.Count = 0 Then
                GoTo whileBreak
            End If
        End If

        ' 4.3. 否则，弹出左括号栈的栈顶 `token`
        ' - 因为可能会进入 2，导致 token 是注释，但是 2 中已经把注释吸收完了，所以这里弹出的一定是注释
        Set token = leftParenthesisStack.pop

        ' 5. 检查: 如果 `offset > 1`
        If offset > 1 Then
            ' 1. 执行: 吸收多个元素到作用域右侧，将 `offset - 1` 个注释元素吸收到 `result` 右侧
            Set matchEnvBoundaryParenthesis = consumeComponentToEnvRight( _
                matchEnvBoundaryParenthesis, tokenScanner, offset - 1)
        End If

        ' 6. `result` 的左边界括号 + 1: `addBoundaryLeftParenthesis()`
        matchEnvBoundaryParenthesis.addBoundaryLeftParenthesis
        ' 7. `result` 的右边界括号 + 1: `addBoundaryRightParenthesis()`
        matchEnvBoundaryParenthesis.addBoundaryRightParenthesis
        ' 8. 向前推进扫描器
        tokenScanner.toNext
    Wend
whileBreak:

    ' ==================================================
    ' 5. 尝试处理栈顶的注释
    ' ==================================================
    ' 1. 如果栈没用完，则继续
    If leftParenthesisStack.Count > 0 Then
        ' 2. 尝试吸收左括号栈栈顶连续出现的单行注释到作用域左侧，结果保存到 `result`
        Set matchEnvBoundaryParenthesis = consumeLPStackTopSingleLineCommentToEnvLeft( _
            matchEnvBoundaryParenthesis, leftParenthesisStack)
    End If

    ' ==================================================
    ' 6. 尝试从右侧吸收一个同行注释
    ' ==================================================
    ' 1. 尝试: 吸收一个同行注释到 result 右侧，结果保存到 `result`
    Set matchEnvBoundaryParenthesis = consumeOneShareLineCommentToEnvRight( _
        matchEnvBoundaryParenthesis, tokenScanner)

    ' ==================================================
    ' 7. 返回 `result`
    ' ==================================================
    ' return result
End Function

' 吸收多余的左括号栈到作用域左侧
'
' @param env 原始作用域
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @return 吸收括号后的作用域
Public Function consumeLeftParenthesisStackAllTokenToEnv( _
    ByRef env As ISqlFormatterElem, _
    ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem
    ' ==================================================
    ' 1. 检查，如果左括号栈为空，则结束，并返回 `env`
    ' ==================================================
    If UtilsStack.isEmptyObjStack(leftParenthesisStack) Then
        Set consumeLeftParenthesisStackAllTokenToEnv = env
        Exit Function
    End If

    ' ==================================================
    ' 2. 初始化
    ' ==================================================
    ' 1. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 consumeLeftParenthesisStackAllTokenToEnv
    Set consumeLeftParenthesisStackAllTokenToEnv = env
    ' 2. token `SqlToken token`
    Dim token As SqlToken

    ' ==================================================
    ' 3. 保存栈内的元素到列对象
    ' ==================================================
    ' 1. 从 i = 0 开始到 leftParenthesisStack.Count - 1 遍历左括号栈
    Dim i As Long
    For i = 0 To leftParenthesisStack.Count - 1
        ' 2. 从 `leftParenthesisStack` 弹出一个 `token`
        Set token = leftParenthesisStack.pop

        ' 3. 检查: 如果 `token` 是左括号: `SqlTokenType.LEFT_PARENTHESIS`，`result` 增加左括号: `addBoundaryLeftParenthesis`
        If token.tokenType = SqlTokenType.LEFT_PARENTHESIS Then
            consumeLeftParenthesisStackAllTokenToEnv.addBoundaryLeftParenthesis

        ' 4. 否则
        Else
            ' 4.1. 检查: 如果无法将 `token` 添加到 `result` 左侧，则将 `result` 包装为容器列对象，并保存到 `result`
            If consumeLeftParenthesisStackAllTokenToEnv.canAddComponentToFirst = False Then
                Set consumeLeftParenthesisStackAllTokenToEnv = _
                    SqlFormatterHelper.wrapperEnvToContainerEnv(consumeLeftParenthesisStackAllTokenToEnv)
            End If

            ' 4.2. 将 `token` 保存到 `result` 的前面
            consumeLeftParenthesisStackAllTokenToEnv.addComponentToFirst token
        End If
    Next

    ' ==================================================
    ' 4. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 将栈中的所有内容保存到作用域末尾
'
' @param env 原始作用域
' @param stack 栈对象 (ObjStack<SqlToken>)
' @return 吸收栈内元素后的作用域
Public Function appendStackToEnv(ByRef env As ISqlFormatterElem, ByRef stack As ObjStack) As ISqlFormatterElem
    ' 1. 检查: 如果是空栈，则退出处理，并返回 `env`
    If UtilsStack.isEmptyObjStack(stack) Then
        ' return env
        Set appendStackToEnv= env
        Exit Function
    End If

    ' 2. 保证作用域的可添加性，结果保存到 `result`
    '    这里直接使用 appendStackToEnv
    Set appendStackToEnv = ensureComponentAdditivityEnv(env)

    ' 3. 从 i = 0 开始到 `stack.Count - 1` 遍历栈内元素
    Dim i As Long
    For i = 0 To stack.Count - 1
        ' 将第 i 个元素添加到作用域
        appendStackToEnv.addComponent stack.Item(i)
    Next

    ' 4. 清空栈对象 `stack.clear()`
    stack.clear

    ' 5. 返回 `result`
    ' return result
End Function

' 将栈中的所有内容导出到一个容器作用域
'
' @param stack 栈对象 (ObjStack<SqlToken>)
' @return 吸收栈内元素的容器作用域
Public Function clearStackToContainerEnv(ByRef stack As ObjStack) As SqlFormatterContainerEnv
    ' 1. 检查: 如果 `stack` 是空栈，则结束，并返回 null
    If UtilsStack.isEmptyObjStack(stack) Then
        ' return null
        Exit Function
    End If

    ' 2. 创建一个容器作用域 `SqlFormatterContainerEnv result`
    '    这里直接使用 clearStackToContainerEnv
    Set clearStackToContainerEnv = New SqlFormatterContainerEnv

    ' 3. 从 i = 0 开始到 `stack.Count - 1` 遍历栈内元素
    Dim i As Long
    For i = 0 To stack.Count - 1
        ' 将第 i 个元素添加到作用域
        clearStackToContainerEnv.ISqlFormatterElem_addComponent stack.Item(i)
    Next

    ' 4. 清空栈对象 `stack.clear()`
    stack.clear

    ' 5. 返回 `result`
    ' return result;
End Function

' 保证列对象的可添加性
'
' @param col 原始列对象
' @return 可添加元素的列对象
Public Function ensureComponentAdditivityCol(ByRef col As ISqlFormatterElem) As ISqlFormatterElem
    ' 1. 检查: 如果 `col` 不存在边界括号
    If Not col.hasBoundaryParenthesis Then
        Set ensureComponentAdditivityCol = col
        Exit Function
    End If

    ' 2. 否则 (`col` 存在边界括号)，将 col 包装为容器列对象，并返回
    Set ensureComponentAdditivityCol = SqlFormatterHelper.wrapperColToContainerCol(col)
End Function

' 吸收一个token到列对象右侧
'
' @param col 列对象
' @param token 需要吸收的 token
' @return 吸收 token 后的列对象
Public Function consumeTokenToColRight(ByRef col As ISqlFormatterElem, ByRef token As SqlToken) As ISqlFormatterElem
    ' 1. 保证作用域的可添加性，结果保存到 `ISqlFormatterElem result`
    Set consumeTokenToColRight = ensureComponentAdditivityCol(col)

    ' 2. 将 `token` 保存到 `result` 的左侧
    consumeTokenToColRight.addComponent token

    ' 3. 结束，返回 `result`
End Function

' 吸收一个同行注释到列对象右侧
'
' @param col 原始作用域对象
' @param tokenScanner 扫描器
Public Function consumeOneShareLineCommentToColRight( _
    ByRef col As ISqlFormatterElem, ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' 1. 检查: 如果扫描器用完，则退出处理，并返回 `col`
    If Not tokenScanner.hasNext Then
        ' return col
        Set consumeOneShareLineCommentToColRight = col
        Exit Function
    End If

    ' 2. 从 `tokenScanner` 中获取一个 `SqlToken token`
    Dim token As SqlToken
    Set token = tokenScanner.peek

    ' 3. 检查: 如果 token 不是同行注释，则退出处理，并返回 `col`: `ISqlFormatterElem_isShareLineComment() == false`
    If token.ISqlFormatterElem_isShareLineComment = False Then
        ' return col
        Set consumeOneShareLineCommentToColRight = col
        Exit Function
    End If

    ' 4. 声明变量
    ' 4.1. 结果对象 `ISqlFormatterElem result`
    '      这里直接使用 consumeOneShareLineCommentToColRight

    ' 5. 设置结果对象 `result = col`
    Set consumeOneShareLineCommentToColRight = col

    ' 6. 检查: 如果 `result` 存在边界括号: `hasBoundaryParenthesis()`
    If consumeOneShareLineCommentToColRight.hasBoundaryParenthesis Then
        ' 6.1. 将 `result` 包装为容器作用域，并返回: `SqlFormatterHelper.wrapperColToContainerCol`
        Set consumeOneShareLineCommentToColRight = SqlFormatterHelper.wrapperColToContainerCol(consumeOneShareLineCommentToColRight)
    End If

    ' 7. 将 `token` 保存到 `result`: `addComponent`
    consumeOneShareLineCommentToColRight.addComponent token

    ' 8. 向前推进扫描器
    tokenScanner.toNext

    ' 9. 退出处理，并返回 `result`
    ' return result
End Function

' 吸收多个元素到列对象右侧
'
' @param col 原始列对象
' @param tokenScanner 扫描器
' @param count 需要吸收的元素数量
Public Function consumeComponentToColRight( _
    ByRef col As ISqlFormatterElem, _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef count As Integer) As ISqlFormatterElem
    ' 1. 声明变量
    ' 1.1. 遍历索引 `int i`
    Dim i As Integer
    ' 1.2. 结果对象 `ISqlFormatterElem result`
    '      这里直接使用 consumeComponentToColRight

    ' 2. 保存列对象到结果对象 `result = col`
    Set consumeComponentToColRight = col

    ' 3. 检查: 如果 `result` 存在边界括号: `hasBoundaryParenthesis() == true`
    If consumeComponentToColRight.hasBoundaryParenthesis Then
        ' 3.1. 将 `result` 包装为容器列对象，并保存到 `result`: `SqlFormatterHelper.wrapperColToContainerCol`
        Set consumeComponentToColRight = SqlFormatterHelper.wrapperColToContainerCol(consumeComponentToColRight)
    End If

    ' 4. 从 `i = 1` 开始到 `count` 遍历
    For i = 1 To count
        ' 4.1. 从扫描器中弹出一个 token，并保存到 `result`
        consumeComponentToColRight.addComponent tokenScanner.getNext
    Next

    ' 5. 退出处理，返回 `result`
    ' return result
End Function

' 从左括号栈吸收多个元素到列对象左侧
'
' @param col 列对象
' @param leftParenthesisStack 左括号栈
' @param count 元素的数量
' @retun 吸收元素后的列对象
Public Function consumeComponentFromStackToColLeft( _
    ByRef col As ISqlFormatterElem, _
    ByRef leftParenthesisStack As ObjStack, _
    ByVal count As Integer) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象
    ' ISqlFormatterElem result
    ' 这里直接使用 consumeComponentFromStackToColLeft

    ' ==================================================
    ' 2. 初始化 `result`
    ' ==================================================
    ' 1. 检查: 如果 `col` 可以在左侧添加元素，则设置 `result = col`
    If col.canAddComponentToFirst Then
        Set consumeComponentFromStackToColLeft = col

    ' 2. 否则，将 `col` 包装为容器列对象，保存到 `result`
    Else
        Set consumeComponentFromStackToColLeft = SqlFormatterHelper.wrapperColToContainerCol(col)
    End If

    ' ==================================================
    ' 3. 遍历吸收元素
    ' ==================================================
    ' 1. 从 `int i = 1` 开始到 `count` 遍历
    Dim i As Integer
    For i = 1 To count
        ' 2. 弹出左括号栈的栈顶元素，并作为普通元素保存到 `result` 的左侧: `addComponentToFirst()`
        consumeComponentFromStackToColLeft.addComponentToFirst leftParenthesisStack.pop
        ' 4. 进入下一次遍历
    Next

    ' ==================================================
    ' 4. 返回 `result`
    ' ==================================================
    ' return result
End Function

' 吸收左括号栈栈顶连续出现的注释到列对象左侧
'
' @param col 列对象
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @retun 吸收括号和注释后的列对象
Public Function consumeLPStackTopCommentToColLeft(ByRef col As ISqlFormatterElem, ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象
    ' ISqlFormatterElem result
    ' 这里直接使用 consumeLPStackTopCommentToColLeft

    ' ==================================================
    ' 2. 初始化 `result`
    ' ==================================================
    ' 1. 检查: 如果 `col` 可以在左侧添加元素，则设置 `result = col`
    If col.canAddComponentToFirst Then
        Set consumeLPStackTopCommentToColLeft = col

    ' 2. 否则，将 `col` 包装为容器列对象，保存到 `result`
    Else
        Set consumeLPStackTopCommentToColLeft = SqlFormatterHelper.wrapperColToContainerCol(col)
    End If

    ' ==================================================
    ' 3. 遍历 `leftParenthesisStack`
    ' ==================================================
    Dim token As SqlToken
    ' 1. 如果 `leftParenthesisStack.Count == 0`，则停止遍历
    While leftParenthesisStack.Count <> 0
        ' 2. 获取左括号栈的栈顶 `token`
        Set token = leftParenthesisStack.peek

        ' 3. 检查，如果 `token` 不是注释，则退出遍历
        If token.ISqlFormatterElem_isComment = False Then
            GoTo whileBreak
        End If

        ' 4. 否则 (是注释)
        ' 4.1. 弹出栈顶
        leftParenthesisStack.pop
        ' 4.2. 将 `token` 作为普通元素保存到 `result` 的左侧
        consumeLPStackTopCommentToColLeft.addComponentToFirst token
        ' 4.3. 进入下一次遍历
    Wend
whileBreak:

    ' ==================================================
    ' 4. 返回 `result`
    ' ==================================================
    ' return result
End Function

' 吸收右侧连续出现的注释到列对象
'
' @param tokenScanner 扫描器
' @param col 列对象
' @reutrn 吸收注释后的列对象
Public Function consumeCommentToColRight( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef col As ISqlFormatterElem) As ISqlFormatterElem

    ' ==================================================
    ' 1. 初始化 `ISqlFormatterElem result = col`
    ' ==================================================
    ' ISqlFormatterElem result = col

    ' 1.`ISqlFormatterElem result = col`
    ' ISqlFormatterElem result = col
    ' 这里直接使用 consumeCommentToColRight
    Set consumeCommentToColRight = col

    ' 2. token `SqlToken token`
    Dim token As SqlToken

    ' ==================================================
    ' 2. 处理下一个元素 (这一步主要用来保证作用域的可加性)
    ' ==================================================
    ' 1. 如果: 扫描器用完了，则退出，并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If

    ' 2. 从 `tokenScanner` 中获取一个 `token`
    Set token = tokenScanner.peek

    ' 3. 检查: 如果 token 是注释: `ISqlFormatterElem_isComment() == true`
    If token.ISqlFormatterElem_isComment Then
        ' 3.1. 保证列对象的可添加性，结果保存到 `result`
        Set consumeCommentToColRight = ensureComponentAdditivityCol(consumeCommentToColRight)
        ' 3.2. 将 `token` 保存到 `result`: `addComponent()`
        consumeCommentToColRight.addComponent token
        ' 3.3. 向前推进扫描器
        tokenScanner.toNext

    ' 4. 否则（不是注释），退出，并返回 `result`
    Else
        Exit Function
    End If

    ' ==================================================
    ' 3. 继续遍历处理右侧的注释
    ' ==================================================
    ' 1. 如果: 扫描器用完了，则退出遍历
    While tokenScanner.hasNext
        ' 2. 从 `tokenScanner` 中获取一个 `token`
        Set token = tokenScanner.peek

        ' 3. 检查: 如果 token 是注释
        If token.ISqlFormatterElem_isComment Then
            ' 3.1. 将 `token` 保存到 `result`: `addComponent()`
            consumeCommentToColRight.addComponent token
            ' 3.2. 向前推进扫描器
            tokenScanner.toNext
            ' 3.3. 进入下一次遍历

        ' 4. 否则（不是注释），则退出遍历
        Else
            GoTo whileBreak
        End If
    Wend
whileBreak:

    ' ==================================================
    ' 4. 返回 `result`
    ' ==================================================
    ' return result
End Function

' 对一个右括号进行配对并生成容器列对象
'
' @param tokenScanner 扫描器
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @return 吸收括号后的列对象
Public Function matchOneRightParenthesis( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem

    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 遍历索引 `long i`
    Dim i As Long
    ' 2. 栈中元素的数量 `long stackMaxIndex`
    Dim stackMaxIndex As Long
    ' 2. token `SqlToken token`
    Dim token As SqlToken
    ' 3. 是否存在左括号 `boolean hasRightParenthesis = false`
    Dim hasRightParenthesis As Boolean
    ' 4. 结果容器对象 `SqlFormatterContainerCol result`
    Dim result As SqlFormatterContainerCol

    ' ==================================================
    ' 2. 检查左括号栈中是否存在左括号
    ' ==================================================
    ' 1. 保存栈中的元素数量
    stackMaxIndex = leftParenthesisStack.MaxIndex
    ' 2. 从 `i = stackMaxIndex` 开始到 `0` 倒序遍历栈中的元素
    For i = stackMaxIndex To 0 Step -1
        ' 3. 从 `leftParenthesisStack` 中获取第 i 个 `token`
        Set token = leftParenthesisStack.Item(i)
        ' 4. 检查: 如果token的类型是 `SqlTokenType.LEFT_PARENTHESIS`
        If token.tokenType = SqlTokenType.LEFT_PARENTHESIS Then
            ' 4.1. 设置 `hasRightParenthesis = true`
            hasRightParenthesis = True
            ' 4.2. 退出遍历
            Exit For
        End If
        ' 5. 否则，从 3 开始下一轮遍历
    Next

    ' ==================================================
    ' 3. 检查是否需要继续
    ' ==================================================
    ' 1. 检查: 如果不存在左括号: `hasRightParenthesis == false`，则退出处理，返回 null
    If hasRightParenthesis = False Then
        ' return null
        Exit Function
    End If

    ' ==================================================
    ' 4. 进行配对
    ' ==================================================
    ' 1. 创建结果对象
    ' result = new SqlFormatterContainerCol()
    Set result = New SqlFormatterContainerCol

    ' 2. 吸收左括号栈中的内容
    ' 2.1. 从 `i` 开始，到 `stackMaxIndex` 遍历
    For i = i To stackMaxIndex
        ' 2.2. 从栈中弹出一个 token
        Set token = leftParenthesisStack.pop
        ' 2.3. 将 token 从左侧添加到 `result`
        result.ISqlFormatterElem_addComponentToFirst token
    Next

    ' 3. 从扫描器中弹出一个 token
    Set token = tokenScanner.getNext

    ' 4. 将 `token` 添加到 `result`
    result.ISqlFormatterElem_addComponent token

    ' ==================================================
    ' 5. 对 `result` 执行: 列对象括号配对，结果保存到 `result`
    ' ==================================================
    Set result = matchColBoundaryParenthesis(result, tokenScanner, leftParenthesisStack)

    ' ==================================================
    ' 6. 退出处理，返回 `result`
    ' ==================================================
    Set matchOneRightParenthesis = result
End Function

' 列对象括号配对
'
' @param col 列对象
' @param tokenScanner 扫描器
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @return 吸收括号后的列对象
Public Function matchColBoundaryParenthesis( _
    ByRef col As ISqlFormatterElem, _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem
    ' ==================================================
    ' 1. 设置 `ISqlFormatterElem result = col`
    ' ==================================================
    ' 这里直接使用 matchColBoundaryParenthesis
    Set matchColBoundaryParenthesis = col

    ' ==================================================
    ' 2. 检查: 如果不存在左括号栈
    ' ==================================================
    If UtilsStack.isEmptyObjStack(leftParenthesisStack) Then
        ' 1. 尝试: 吸收一个同行注释到 result 右侧，结果保存到 `result`
        Set matchColBoundaryParenthesis = consumeOneShareLineCommentToColRight( _
            matchColBoundaryParenthesis, tokenScanner)

        ' 2. 退出处理，并返回 `result`
        Exit Function
    End If

    ' ==================================================
    ' 3. 声明变量
    ' ==================================================
    ' 1. 偏移量 `int offset`
    Dim offset As Integer
    ' 2. 左括号在括号栈中的偏移量 `int offsetLeft`
    Dim offsetLeft As Integer

    ' ==================================================
    ' 4. 遍历处理 `)`
    ' ==================================================
    ' 1. 如果: 扫描器用完了，或者左括号栈用完，则退出遍历
    While leftParenthesisStack.Count <> 0 And tokenScanner.hasNext
        ' 2. 执行: 获取扫描器到下一个最近的右括号的偏移量，结果保存到 `offset`
        offset = SqlFormatterHelper.getNextRightParenthesisOffset(tokenScanner)

        ' 3. 检查: 如果 `offset == 0`，则退出遍历
        If offset = 0 Then
            GoTo whileBreak
        End If

        ' 4. 进行括号配对 (现在发现了一个右括号)
        ' 4.1. 执行: 获取左括号栈中下一个左括号的偏移量，结果保存到 `offsetLeft`
        offsetLeft = SqlFormatterHelper.getLeftParenthesisOffsetInStack(leftParenthesisStack)
        ' 4.2. 检查: 如果 `offsetLeft == 0`，则退出遍历
        If offsetLeft = 0 Then
            GoTo whileBreak

        ' 4.3. 否则，检查: 如果 `offsetLeft > 1`，则执行: 从左括号栈吸收多个元素到列对象左侧，结果保存到 `result`
        ElseIf offsetLeft > 1 Then
            Set matchColBoundaryParenthesis = consumeComponentFromStackToColLeft( _
                matchColBoundaryParenthesis, leftParenthesisStack, offsetLeft - 1)
        End If

        ' 4.4. 弹出左括号栈的栈顶 `token`
        leftParenthesisStack.pop

        ' 5. 检查: 如果 `offset > 1`
        If offset > 1 Then
            ' 1. 执行: 吸收多个元素到列对象右侧，将 `offset - 1` 个注释元素吸收到 `result` 右侧
            Set matchColBoundaryParenthesis = consumeComponentToColRight( _
                matchColBoundaryParenthesis, tokenScanner, offset - 1)
        End If

        ' 6. 检查: 如果 `result` 的类型是 `SqlToken`，则将列包装为容器列，结果保存到 `result`: `SqlFormatterHelper.wrapperColToContainerCol`
        If TypeOf matchColBoundaryParenthesis Is SqlToken Then
            Set matchColBoundaryParenthesis = SqlFormatterHelper.wrapperColToContainerCol(matchColBoundaryParenthesis)
        End If
        ' 7. `result` 的左边界括号 + 1: `addBoundaryLeftParenthesis()`
        matchColBoundaryParenthesis.addBoundaryLeftParenthesis
        ' 8. `result` 的右边界括号 + 1: `addBoundaryRightParenthesis()`
        matchColBoundaryParenthesis.addBoundaryRightParenthesis
        ' 9. 向前推进扫描器
        tokenScanner.toNext
    Wend
whileBreak:

    ' ==================================================
    ' 5. 尝试从右侧吸收一个同行注释
    ' ==================================================
    ' 1. 尝试: 吸收一个同行注释到 result 右侧，结果保存到 `result`
    Set matchColBoundaryParenthesis = consumeOneShareLineCommentToColRight( _
        matchColBoundaryParenthesis, tokenScanner)

    ' ==================================================
    ' 6. 返回 `result`
    ' ==================================================
    ' return result
End Function

' 吸收多余的左括号栈到列对象左侧
'
' @param col 列对象
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @return 吸收括号后的列对象
Public Function consumeLeftParenthesisStackAllTokenToCol( _
    ByRef col As ISqlFormatterElem, _
    ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem
    ' ==================================================
    ' 1. 检查，如果左括号栈为空，则结束，并返回 `col`
    ' ==================================================
    If UtilsStack.isEmptyObjStack(leftParenthesisStack) Then
        Set consumeLeftParenthesisStackAllTokenToCol = col
        Exit Function
    End If

    ' ==================================================
    ' 2. 初始化
    ' ==================================================
    ' 1. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 consumeLeftParenthesisStackAllTokenToCol
    Set consumeLeftParenthesisStackAllTokenToCol = col
    ' 2. token `SqlToken token`
    Dim token As SqlToken

    ' ==================================================
    ' 3. 保存栈内的元素到列对象
    ' ==================================================
    ' 1. 从 i = 0 开始到 leftParenthesisStack.Count - 1 遍历左括号栈
    Dim i As Long
    For i = 0 To leftParenthesisStack.Count - 1
        ' 2. 从 `leftParenthesisStack` 弹出一个 `token`
        Set token = leftParenthesisStack.pop

        ' 3. 检查: 如果 `token` 是左括号: `SqlTokenType.LEFT_PARENTHESIS`，`result` 增加左括号: `addBoundaryLeftParenthesis`
        If token.tokenType = SqlTokenType.LEFT_PARENTHESIS Then
            consumeLeftParenthesisStackAllTokenToCol.addBoundaryLeftParenthesis

        ' 4. 否则
        Else
            ' 4.1. 检查: 如果无法将 `token` 添加到 `result` 左侧，则将 `result` 包装为容器列对象，并保存到 `result`
            If consumeLeftParenthesisStackAllTokenToCol.canAddComponentToFirst = False Then
                Set consumeLeftParenthesisStackAllTokenToCol = _
                    SqlFormatterHelper.wrapperColToContainerCol(consumeLeftParenthesisStackAllTokenToCol)
            End If

            ' 4.2. 将 `token` 保存到 `result` 的前面
            consumeLeftParenthesisStackAllTokenToCol.addComponentToFirst token
        End If
    Next

    ' ==================================================
    ' 4. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 将栈中的所有内容保存到列对象末尾
'
' @param col 列对象
' @param stack 栈对象 (ObjStack<SqlToken>)
' @return 吸收栈元素后的列对象
Public Function appendStackToCol( _
    ByRef col As ISqlFormatterElem, _
    ByRef stack As ObjStack) As ISqlFormatterElem
    ' 1. 检查: 如果是空栈，则退出处理，并返回 `col`
    If UtilsStack.isEmptyObjStack(stack) Then
        ' return col
        Set appendStackToCol = col
        Exit Function
    End If

    ' 2. 保证列对象的可添加性，结果保存到 `result`
    '    这里直接使用 appendStackToCol
    Set appendStackToCol = ensureComponentAdditivityCol(col)

    ' 3. 从 i = 0 开始到 `stack.Count - 1` 遍历栈内元素
    Dim i As Long
    For i = 0 To stack.Count - 1
        ' 将第 i 个元素添加到作用域
        appendStackToCol.addComponent stack.Item(i)
    Next

    ' 4. 清空栈对象 `stack.clear()`
    stack.clear

    ' 5. 返回 `result`
    ' return result
End Function

' 将栈中的所有内容导出到一个列对象
'
' @param stack 栈对象 (ObjStack<SqlToken>)
' @return 吸收所有栈元素的容器列对象
Public Function clearStackToContainerCol(ByRef stack As ObjStack) As SqlFormatterContainerCol
    ' 1. 检查: 如果 `stack` 是空栈，则结束，并返回 null
    If UtilsStack.isEmptyObjStack(stack) Then
        ' return null
        Exit Function
    End If

    ' 2. 创建一个容器列对象 `SqlFormatterContainerCol result`
    Set clearStackToContainerCol = New SqlFormatterContainerCol

    ' 3. 从 i = 0 开始到 `stack.Count - 1` 遍历栈内元素
    Dim i As Long
    For i = 0 To stack.Count - 1
        ' 将第 i 个元素添加到列对象
        clearStackToContainerCol.ISqlFormatterElem_addComponent stack.Item(i)
    Next

    ' 4. 清空栈对象 `stack.clear()`
    stack.clear

    ' 5. 返回 `result`
    ' return result;
End Function

' 将两个元素合并为多条件组合对象
'
' @param core 条件对象或者是多条件组合对象 (改参数将会作为合并的核心组件来使用)
' @param newCondition 需要合并的条件对象
' @return 合并后的条件组合对象
Public Function mergeTwoElemToMultiConditions( _
    ByRef core As ISqlFormatterElem, _
    ByRef newCondition As ISqlFormatterElem) As ISqlFormatterElem

    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `SqlFormatterMultiConditions result`
    Dim result As SqlFormatterMultiConditions

    ' ==================================================
    ' 2. 获取多条件结果对象
    ' ==================================================
    ' 1. 检查: 如果 `core` 是 null
    If core Is Nothing Then
        ' 1.1. 创建一个结果对象 `result = new SqlFormatterMultiConditions()`
        Set result = New SqlFormatterMultiConditions

    ' 2. 否则，检查: 如果 `core` 的类型是 `SqlFormatterMultiConditions`，则进行类型转换并保存到 `result`
    ElseIf Typeof core Is SqlFormatterMultiConditions Then
        Set result = core

    ' 3. 否则
    Else
        ' 2.1. 创建一个结果对象 `result = new SqlFormatterMultiConditions()`
        Set result = New SqlFormatterMultiConditions
        ' 2.2. 保存 `core` 到 `result` : `addCondition`
        result.addCondition core
    End If

    ' ==================================================
    ' 3. 处理 `newCondition`
    ' ==================================================
    ' 1. 保存 `newCondition` 到 `result`
    result.addCondition newCondition

    ' ==================================================
    ' 4. 退出处理，返回 `result`
    ' ==================================================
    Set mergeTwoElemToMultiConditions = result
End Function

' 创建一个只有逻辑运算符和第一个元素的的条件
'
' @param logicOp 逻辑运算符
' @param firstElem 条件的第一个元素
' @return 只有逻辑运算符和第一个元素的的条件
Public Function createOneElemCondition( _
    ByRef logicOp As SqlToken, _
    ByRef firstElem As ISqlFormatterElem) As SqlFormatterTwoElemCondition
    Set createOneElemCondition = New SqlFormatterTwoElemCondition

    createOneElemCondition.ISqlFormatterCondition_setLogicOperator logicOp
    createOneElemCondition.ISqlFormatterElem_addComponent firstElem
End Function

' 创建一个IS_NOT组合关键字
'
' @param isToken Is 关键字元素
' @param notToken Not 关键字元素
' @return IS NOT 组合关键字
Public Function createIsNotConditionOp(ByRef isToken As SqlToken, ByRef notToken As SqlToken) As SqlFormatterTokenGroup
    Set createIsNotConditionOp = New SqlFormatterTokenGroup

    createIsNotConditionOp.ISqlFormatterElem_addComponent isToken
    createIsNotConditionOp.ISqlFormatterElem_addComponent notToken

    createIsNotConditionOp.setType isToken.tokenType
End Function

' ======================================================================
' Sql 解析
' ======================================================================

' 入口1--解析方式1: 通过 sql 字符串完成解析
'
' @param sql  需要格式化的 sql
Public Sub analyze(ByRef sql As String)
    ' ==================================================
    ' 1. 清除缓存
    ' ==================================================
    clearCacheData

    ' ==================================================
    ' 2. 快速结束
    ' ==================================================
    ' 1. 检查: 如果参数 sql 是空字符串，则退出处理
    If sql = "" Then
        ' return
        Exit Sub
    End If

    ' ==================================================
    ' 3. 执行 sql 分词
    ' ==================================================
    ' 1. 对参数 sql 做分词
    extractorSqlToken sql

    ' 2. 检查: 如果分词后的结果中没有元素，则退出处理
    If multiSqlMeanTokenCache.Count <= 0 Then
        ' return
        Exit Sub
    End If

    ' ==================================================
    ' 4. 执行解析
    ' ==================================================
    doAnalyzeMultiSqlToken
End Sub

' 入口2--解析方式2: 直接解析 sql token 数组
'
' @param meanTokens  需要格式化的 sql token 数组 (IObjArrayRef<SqlToken>)
Public Sub analyzeTokens(ByRef meanTokens As IObjArrayRef)
    ' ==================================================
    ' 1. 清除缓存
    ' ==================================================
    clearCacheData

    ' ==================================================
    ' 2. 快速结束
    ' ==================================================
    ' 1. 检查: 如果参数的元素数组是空，则退出处理
    If meanTokens Is Nothing Then
        ' return
        Exit Sub

    ElseIf meanTokens.Count <= 0 Then
        ' return
        Exit Sub
    End If

    ' ==================================================
    ' 3. 分解 sql token
    ' ==================================================
    ' 1. 按照逗号分隔 sql token，结果缓存到属性 `multiSqlMeanTokenCache`
    Set multiSqlMeanTokenCache = splitTokensBySemicolon(meanTokens)

    ' ==================================================
    ' 4. 执行解析
    ' ==================================================
    doAnalyzeMultiSqlToken
End Sub

' 按照分号拆分sql
'
' @param meanTokens sql token集合 (IObjArrayRef<SqlToken>)
' @return 按照分号拆分sql token 集合 IObjArrayRef<IObjArrayRef<SqlToken>>
Private Function splitTokensBySemicolon(ByRef meanTokens As IObjArrayRef) As IObjArrayRef
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `IObjArrayRef<IObjArrayRef<SqlToken>> result`
    '    这里直接使用 splitTokensBySemicolon
    ' 2. sql token 集合的最大索引 `long meanTokensMaxIndex`
    Dim meanTokensMaxIndex As Long
    ' 3. 索引标记 `long markIndex`
    Dim markIndex As Long
    ' 4. 遍历索引 `long i`
    Dim i As Long
    ' 5. token `SqlToken token`
    Dim token As SqlToken

    ' ==================================================
    ' 2. 初始化
    ' ==================================================
    ' 1. 保存 sql token 集合的最大索引 `meanTokensMaxIndex`
    meanTokensMaxIndex = meanTokens.MaxIndex
    ' 2. 设置标记索引 `markIndex` 为 -1
    markIndex = -1

    ' ==================================================
    ' 3. 获取第一个分号的位置
    ' ==================================================
    ' 1. 从 `i = 0` 开始到 `meanTokensMaxIndex` 遍历参数 `meanTokens`
    For i = 0 To meanTokensMaxIndex
        ' 2. 获取第 i 个 `token`
        Set token = meanTokens.Item(i)

        ' 3. 检查: 如果 token 类型是分号
        If token.tokenType = SqlTokenType.SEMICOLON Then
            ' 3.1. 保存分号的索引到 `markIndex`
            markIndex = i
            ' 3.2. 退出遍历
            Exit For
        End If
    Next

    ' ==================================================
    ' 4. 处理只有一个 sql 的情况
    ' ==================================================
    ' 1. 检查: 如果不存在分号: `markIndex == -1`
    If markIndex = -1 Then
        ' 1.1. 将参数 `meanTokens` 包装为长度为 1 的数组引用，结果保存到 `result`
        Set splitTokensBySemicolon = UtilsArrayRef.wrapperToObjArrayRef(meanTokens)
        ' 1.2. 退出处理，返回 `result`
        ' return result
        Exit Function

    ' 2. 否则，检查: 如果分号是最后一个元素: `markIndex == meanTokensMaxIndex`
    ElseIf markIndex = meanTokensMaxIndex Then
        ' 2.1. 拷贝 `meanTokens[0, markIndex - 1]` 的引用，并包装为长度为 1 的数组引用，
        '      结果保存到 `result`: `UtilsArrayRef.wrapperToObjArrayRef`
        Set splitTokensBySemicolon = UtilsArrayRef.wrapperToObjArrayRef(meanTokens.copyToNewArray(0, markIndex))
        ' 2.2. 退出处理，返回 `result`
        ' return result
        Exit Function
    End If

    ' ==================================================
    ' 5. 处理有多个 sql 的情况
    ' ==================================================
    ' ==============================
    ' 5.1. 初始化结果对象 `result`
    ' ==============================
    Set splitTokensBySemicolon = UtilsArrayRef.createRealObjArrayRef

    ' ==============================
    ' 5.2. 保存第一个 sql 的 token (不需要分号 token)
    ' ==============================
    ' 1. 拷贝 `meanTokens[0, markIndex - 1]`，并保存到 `result`
    splitTokensBySemicolon.Add meanTokens.copyToNewArray(0, markIndex)
    ' 2. 向后移动标记索引: `markIndex += 1`
    markIndex = markIndex + 1

    ' ==============================
    ' 5.3. 遍历并分割 sql
    ' ==============================
    ' 1. 从 `i = markIndex` 到 `meanTokensMaxIndex` 遍历 `meanTokens`
    For i = markIndex To meanTokensMaxIndex
        ' 2. 获取第 i 个 `token`
        Set token = meanTokens.Item(i)

        ' 3. 检查: 如果 token 的类型是分号
        If token.tokenType = SqlTokenType.SEMICOLON Then
            ' 3.1. 拷贝 `meanTokens[markIndex, i - 1]`，并保存到 `result`
            splitTokensBySemicolon.Add meanTokens.copyToNewArray(markIndex, i)
            ' 3.2. 保存标记索引为 `markIndex = i + 1`
            markIndex = i + 1
        End If
    Next

    ' ==============================
    ' 5.4. 处理未吸收的 token
    ' ==============================
    ' 1. 检查: 如果标记索引没有移动到最后，则继续: `markIndex <= meanTokensMaxIndex`
    If markIndex <= meanTokensMaxIndex Then
        ' 2. 从 `markIndex` 开始拷贝 `meanTokens` 的剩余元素，并保存到 `result`
        splitTokensBySemicolon.Add meanTokens.copyToNewArray(markIndex)
    End If

    ' ==================================================
    ' 6. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 启动 sql 解析
Private Sub doAnalyzeMultiSqlToken()
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 每个 sql 的解析结果 `ISqlFormatterElem elem`
    Dim elem As ISqlFormatterElem
    ' 2. 遍历索引 `long i`
    Dim i As long
    ' 3. 每个 sql 的集合对象 `IObjArrayRef<SqlToken> sqlMeanTokens`
    Dim sqlMeanTokens As IObjArrayRef

    ' ==================================================
    ' 2. 初始化
    ' ==================================================
    ' 1. 初始化容器作用域缓存 `sqlEnvCache`
    Set sqlEnvCache = UtilsArrayRef.createRealObjArrayRef(8)

    ' ==================================================
    ' 3. 生成所有 sql 的作用域
    ' ==================================================
    ' 1. 从 `i = 0` 开始到 `multiSqlMeanTokenCache.MaxIndex`，遍历 `multiSqlMeanTokenCache`
    For i = 0 To multiSqlMeanTokenCache.MaxIndex
        ' 2. 获取第 i 个 sql 的 token 集合到 `sqlMeanTokens`
        Set sqlMeanTokens = multiSqlMeanTokenCache.Item(i)

        ' 3. 解析 sql
        ' 3.1. 生成作用域，结果保存到 `elem`
        Set elem = createEnv(sqlMeanTokens)
        ' 3.2. 设置这个作用域的嵌套级别
        elem.setElemNestCount 0, config

        ' 4. 将作用域对象保存到 `sqlEnvCache`
        sqlEnvCache.Add elem
    Next
End Sub

' 解析 sql 的驱动处理---外层逻辑 -- 包装扫描器
'
' @param meanTokens 需要解析的 sql 的 token 集合 (IObjArrayRef<SqlToken>)
' @return sql 解析后生成的作用域对象
Public Function createEnv(ByRef meanTokens As IObjArrayRef) As ISqlFormatterElem
    ' 1. 将 `meanTokens` 包装为扫描器
    Dim scanner As ObjArrayRefScanner
    Set scanner = New ObjArrayRefScanner
    scanner.Init meanTokens

    ' 2. 创建作用域 `env`
    Set createEnv = doCreateEnv(scanner)

    ' 3. 返回 `env`
    ' return env;
End Function

' 解析 sql 的驱动处理---内层逻辑 -- 吸收所有 token 并生成作用域
'
' @param tokenScanner 扫描器
' @return 解析 sql 后生成的作用域对象
Private Function doCreateEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 基础解析结果 `ISqlFormatterElem baseElem`
    Dim baseElem As ISqlFormatterElem
    ' 2. 容器作用域 `ISqlFormatterElem containerEnv`
    Dim containerEnv As ISqlFormatterElem

    ' ==================================================
    ' 2. 尝试解析一个 sql 作用域
    ' ==================================================
    ' 1. 执行: 生成sql作用域的基础驱动--剥离左括号，结果保存到 `baseElem`
    Set baseElem = parseSqlEnv(tokenScanner)

    ' ==================================================
    ' 3. 如果 `baseElem` 解析失败
    ' ==================================================
    ' 1. 检查: 如果 `baseElem == null`，则继续
    If baseElem Is Nothing Then
        ' 2. 执行: 兜底处理，结果保存到 `baseElem`
        Set baseElem = parseSqlEnvFallback(tokenScanner)
    End If

    ' ==================================================
    ' 4. 快速结束或创建容器
    ' ==================================================
    ' 1. 吸收右侧连续出现的注释到作用域到 `baseElem`
    Set baseElem = consumeCommentToEnvRight(tokenScanner, baseElem)

    ' 2. 检查: 如果扫描器用完了，则结束，并返回 `baseElem`
    If Not tokenScanner.hasNext Then
        ' return baseElem
        Set doCreateEnv = baseElem
        Exit Function
    End If

    ' 3. 创建一个容器
    ' 3.1. 检查: 如果 baseElem 的类型不是 `SqlFormatterContainerEnv`
    If TypeOf baseElem Is SqlFormatterContainerEnv Then
        ' 3.1.1. 创建一个容器作用域 `containerEnv = SqlFormatterHelper.createContainerEnv`
        Set containerEnv = SqlFormatterHelper.createContainerEnv
        ' 3.1.2. 将 `baseElem` 保存到 `containerEnv`
        containerEnv.addComponent baseElem

    ' 3.2. 否则，检查: 如果 baseElem 存在边界括号: `hasBoundaryParenthesis() == true`
    ElseIf baseElem.hasBoundaryParenthesis Then
        ' 3.2.1. 创建一个容器作用域 `containerEnv = SqlFormatterHelper.createContainerEnv`
        Set containerEnv = SqlFormatterHelper.createContainerEnv
        ' 3.2.2. 将 `baseElem` 保存到 `containerEnv`
        containerEnv.addComponent baseElem

    ' 3.3. 否则，设置 `containerEnv = baseElem`
    Else
        Set containerEnv = baseElem
    End If

    ' ==================================================
    ' 5. 继续遍历，直到扫描器被用完
    ' ==================================================
    ' 1. 如果扫描器用完了，则退出遍历
    While tokenScanner.hasNext
        ' 2. 尝试解析一个 sql 作用域
        Set baseElem = parseSqlEnv(tokenScanner)

        ' 3. 检查，如果 `baseElem == null`
        If baseElem Is Nothing Then
            ' 3.1. 执行: 兜底处理，结果保存到 `baseElem`
            Set baseElem = parseSqlEnvFallback(tokenScanner)
        End If

        ' 4. 将 `baseElem` 保存到 `containerEnv`: `containerEnv.addComponent(baseElem)`
        containerEnv.addComponent baseElem
        ' 5. 回到 1 继续遍历
    Wend

    ' ==================================================
    ' 6. 退出处理，返回 `containerEnv`
    ' ==================================================
    ' return containerEnv;
    Set doCreateEnv = containerEnv
End Function

' 兜底处理--parseSqlEnvFallback--在解析失败时，强制解析
'
' @param tokenScanner 扫描器
' @return 解析 sql 后生成的作用域
Public Function parseSqlEnvFallback(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. token `SqlToken token`
    Dim token As SqlToken
    ' 2. tokenType `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType
    ' 3. 左括号栈 `ObjStack<SqlToken> leftParenthesisStack`
    Dim leftParenthesisStack As ObjStack
    ' 4. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 parseSqlEnvFallback

    ' ==================================================
    ' 2. 初始化
    ' ==================================================
    ' 1. 从扫描器中获取一个 `token`
    Set token = tokenScanner.peek
    ' 2. 获取 token 的类型到 `tokenType`
    tokenType = token.tokenType

    ' ==================================================
    ' 3. 检查: 如果 `tokenType` 是注释: `SqlHelper.isCommentToken(tokenType) == true`
    ' ==================================================
    If SqlHelper.isCommentToken(tokenType) Then
        ' 1. 向前推进扫描器
        tokenScanner.toNext
        ' 2. 退出处理，并返回 `token`
        ' return token
        Set parseSqlEnvFallback = token
        Exit Function
    End If

    ' ==================================================
    ' 4. 检查: 如果 `tokenType` 是右括号: `SqlTokenType.RIGHT_PARENTHESIS`
    ' ==================================================
    If tokenType = SqlTokenType.RIGHT_PARENTHESIS Then
        ' 1. 向前推进扫描器
        tokenScanner.toNext
        ' 2. 退出处理，并返回 `token`
        ' return token
        Set parseSqlEnvFallback = token
        Exit Function
    End If

    ' ==================================================
    ' 5. 检查：如果 `tokenType` 是左括号: `SqlTokenType.LEFT_PARENTHESIS`
    ' ==================================================
    ' 1. 解析开头连续出现的左括号，结果保存到 `leftParenthesisStack`
    Set leftParenthesisStack = parseStartLeftParenthesis(tokenScanner)
    ' 2. 检查: 如果扫描器已经用完
    If Not tokenScanner.hasNext Then
        ' 2.1. 将左括号栈转换成一个容器作用域，调用 `result = clearStackToContainerEnv()`
        Set parseSqlEnvFallback = clearStackToContainerEnv(leftParenthesisStack)
        ' 2. 结束，并返回 `result`
        ' return result
        Exit Function
    End If

    ' ==================================================
    ' 6. 按照起始元素的类型来进行解析
    ' ==================================================
    ' 1. 检查: 如果 `tokenType` 是 `SqlTokenType.KEY_SELECT`，解析一个select，并保存到 `result`
    If tokenType = SqlTokenType.KEY_SELECT Then
        Set parseSqlEnvFallback = parseSelect(tokenScanner)

    ' 2. 否则，检查: 如果 `tokenType` 是 `SqlTokenType.KEY_FROM`，解析from，并保存到 `result`
    ElseIf tokenType = SqlTokenType.KEY_FROM Then
        Set parseSqlEnvFallback = parseFromEnv(tokenScanner)

    ' 3. 否则，检查: 如果 `tokenType` 是 `SqlTokenType.KEY_WHERE`，解析条件作用域--parseConditionEnv，并保存到 `result`
    ElseIf tokenType = SqlTokenType.KEY_WHERE Then
        Set parseSqlEnvFallback = parseConditionEnv(tokenScanner)

    ' 4. 否则，检查: 如果 `tokenType` 是 left / right / inner / cross / join ，解析join作用域，并保存到 `result`
    ElseIf SqlHelper.tokenTypeIsJoinStartSqlKey(tokenType) Then
        Set parseSqlEnvFallback = parseJoinEnv(tokenScanner)

    ' 5. 否则，检查: 如果 `tokenType` 是 `SqlTokenType.KEY_ON`
    ElseIf tokenType = SqlTokenType.KEY_ON Then
        ' 5.1. 检查: 如果是ON_DUPLICATE作用域，则 解析ON_DUPLICATE作用域，并保存到 `result`
        If SqlFormatterHelper.isOnDuplicateEnv(tokenScanner) Then
            Set parseSqlEnvFallback = parseInsertOnDuplicateEnv(tokenScanner)

        ' 5.2. 否则，解析条件作用域，并保存到 `result`
        Else
            Set parseSqlEnvFallback = parseConditionEnv(tokenScanner)
        End If

    ' 6. 否则，检查: 如果 `tokenType` 是 `SqlTokenType.KEY_GROUP_BY`，解析groupby作用域，并保存到 `result`
    ElseIf tokenType = SqlTokenType.KEY_GROUP_BY Then
        Set parseSqlEnvFallback = parseGroupByEnv(tokenScanner)

    ' 7. 否则，检查: 如果 `tokenType` 是 `SqlTokenType.KEY_HAVING`，解析条件作用域--parseConditionEnv，并保存到 `result`
    ElseIf tokenType = SqlTokenType.KEY_HAVING Then
        Set parseSqlEnvFallback = parseConditionEnv(tokenScanner)

    ' 8. 否则，检查: 如果 `tokenType` 是 `SqlTokenType.KEY_ORDER_BY`，解析orderby作用域，并保存到 `result`
    ElseIf tokenType = SqlTokenType.KEY_ORDER_BY Then
        Set parseSqlEnvFallback = parseOrderByEnv(tokenScanner)

    ' 9. 否则，检查: 如果 `tokenType` 是 `SqlTokenType.KEY_LIMIT`，解析limit作用域，并保存到 `result`
    ElseIf tokenType = SqlTokenType.KEY_LIMIT Then
        Set parseSqlEnvFallback = parseLimitEnv(tokenScanner)

    ' 10. 否则，检查: 如果 `tokenType` 是 `SqlTokenType.KEY_UNION`，解析并吸收union开始的作用域，并保存到 `result`
    ElseIf tokenType = SqlTokenType.KEY_UNION Then
        Set parseSqlEnvFallback = New SqlFormatterContainerEnv
        Set parseSqlEnvFallback = parseFromUnionEnv(tokenScanner, leftParenthesisStack, parseSqlEnvFallback)
    ' 11. 否则，检查: 如果 `tokenType` 是 `SqlTokenType.KEY_UPDATE`，解析一个update，并保存到 `result`
    ElseIf tokenType = SqlTokenType.KEY_UPDATE Then
        Set parseSqlEnvFallback = parseUpdate(tokenScanner)

    ' 12. 否则，检查: 如果 `tokenType` 是 `SqlTokenType.KEY_SET`，解析update的set作用域，并保存到 `result`
    ElseIf tokenType = SqlTokenType.KEY_SET Then
        Set parseSqlEnvFallback = parseUpdateSetEnv(tokenScanner)

    ' 13. 否则，检查: 如果 `tokenType` 是 `SqlTokenType.KEY_USING`，[解析using](#解析using)，并保存到 `result`
    ElseIf tokenType = SqlTokenType.KEY_USING Then
        Set parseSqlEnvFallback = parseUsingEnv(tokenScanner)

    ' 14. 否则，检查: 如果 `tokenType` 是 `SqlTokenType.KEY_DELETE`，解析一个delete，并保存到 `result`
    ElseIf tokenType = SqlTokenType.KEY_DELETE Then
        Set parseSqlEnvFallback = parseDelete(tokenScanner)

    ' 15. 否则，检查: 如果 `tokenType` 是 `SqlTokenType.KEY_INSERT`，解析一个insert，并保存到 `result`
    ElseIf tokenType = SqlTokenType.KEY_INSERT Then
        Set parseSqlEnvFallback = parseInsert(tokenScanner)

    ' 15. 否则，检查: 如果 `SqlTokenType.KEY_INTO`，解析into作用域，并保存到 `result`
    ElseIf tokenType = SqlTokenType.KEY_INTO Then
        Set parseSqlEnvFallback = parseIntoEnv(tokenScanner)

    ' 16. 否则，检查: 如果 `tokenType` 是 `SqlTokenType.KEY_VALUES` 或 `SqlTokenType.KEY_VALUE`，[解析insert的values](#解析insert的values)，并保存到 `result`
    ElseIf tokenType = SqlTokenType.KEY_VALUES Or tokenType = SqlTokenType.KEY_VALUE Then
        Set parseSqlEnvFallback = parseInsetValuesEnv(tokenScanner)

    ' 17. 否则，检查: 如果 `tokenType` 是 use/ingore/force，匹配index_hint_list，并保存到 `result`
    ElseIf tokenType = SqlTokenType.KEY_USE _
        Or tokenType = SqlTokenType.KEY_IGNORE _
        Or tokenType = SqlTokenType.KEY_FORCE Then
        Set parseSqlEnvFallback = parseIndexHintList(tokenScanner)

    ' 18. 否则，检查: 如果 `tokenType` 是 `SqlTokenType.KEY_CAST` ，匹配cast函数--parseCastFunc](#匹配cast函数--parseCastFunc)，并保存到 `result`
    ElseIf tokenType = SqlTokenType.KEY_CAST Then
        Set parseSqlEnvFallback = parseCastFunc(tokenScanner, leftParenthesisStack)


    ' 19. 否则，检查: 如果 `tokenType` 是 `SqlTokenType.KEY_AS`
    ElseIf tokenType = SqlTokenType.KEY_CAST Then
        ' 19.1. 设置 `result = token`
        Set parseSqlEnvFallback = token
        ' 19.2. 扫描器向前推进
        tokenScanner.toNext

    ' 20. 否则，进行最后的尝试
    Else
        ' 20.1. 记录当前扫描器的活动索引: `long tokenScannerActiveIndex = tokenScanner.ActiveIndex`
        Dim tokenScannerActiveIndex As Long
        tokenScannerActiveIndex = tokenScanner.ActiveIndex

        ' 20.2. 从运算符开始匹配表达式--parseExprColStartWithOperator，结果保存到 `result`
        Set parseSqlEnvFallback = parseExprColStartWithOperator(Nothing, leftParenthesisStack, tokenScanner)

        ' 20.3. 检查: 如果扫描器没有推进，则继续: `tokenScannerActiveIndex == tokenScanner.ActiveIndex`
        If tokenScannerActiveIndex = tokenScanner.ActiveIndex Then
            ' 20.4. 匹配一个单独的列对象---doParseSingleCol--执行解析的部分，结果保存到 `result`
            Set parseSqlEnvFallback = doParseSingleCol(tokenScanner, leftParenthesisStack)
        End If
    End If

    ' ==================================================
    ' 7. 解析失败时，强制获取一个元素
    ' ==================================================
    ' 1. 检查: 如果 `result` 是空，继续
    If parseSqlEnvFallback Is Nothing Then
        ' 2. 扫描器向前推进
        tokenScanner.toNext
        ' 3. 设置 `result = token`
        Set parseSqlEnvFallback = token
    End If

    ' ==================================================
    ' 8. 括号配对、吸收左括号栈
    ' ==================================================
    ' 1. 检查如果存在左括号栈，则继续: `leftParenthesisStack != null`
    If Not leftParenthesisStack Is Nothing Then
        ' 2. 检查: 如果左括号栈没用完: `leftParenthesisStack.Count > 0`
        If leftParenthesisStack.Count > 0 Then
            ' 2.1. 执行: 列对象括号配对，结果保存到 `result`
            Set parseSqlEnvFallback = matchColBoundaryParenthesis(parseSqlEnvFallback, tokenScanner, leftParenthesisStack)

            ' 2.2. 检查: 如果左括号没用完，则继续: `leftParenthesisStack.Count > 0`
            If leftParenthesisStack.Count > 0 Then
                ' 2.3. 吸收多余的左括号栈到列对象左侧，结果保存到 `result`
                Set parseSqlEnvFallback = consumeLeftParenthesisStackAllTokenToCol(parseSqlEnvFallback, leftParenthesisStack)
            End If
        End If
        ' 3. 将左括号栈释放回池中: `UtilsPool.releaseObjStack(leftParenthesisStack)`
        UtilsPool.releaseObjStack leftParenthesisStack
    End If

    ' ==================================================
    ' 9. 结束，返回 `result`
    ' ==================================================
    ' return result
End Function

' 解析 sql 的 token 并生成作用域---生成sql作用域的基础驱动--剥离左括号
'
' @param tokenScanner 扫描器
' @optional-param needParseUnion 是否需要解析 union (如果出现了括号，则括号内的union仍然需要解析)
'                 默认值 = true
' @return 解析 sql 后生成的作用域
Public Function parseSqlEnv( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    Optional ByVal needParseUnion As Boolean = True) As ISqlFormatterElem
    ' ==================================================
    ' 1. 检查参数
    ' ==================================================
    ' 1. 如果扫描器已经用完，则立刻结束，并返回 null
    If Not tokenScanner.hasNext Then
        ' return null;
        Exit Function
    End If

    ' ==================================================
    ' 2. 声明变量
    ' ==================================================
    ' 1. 声明一个: 左括号栈 `ObjStack<SqlToken> leftParenthesisStack`, 用来保存连续出现的 `(`
    Dim leftParenthesisStack As ObjStack
    ' 2. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 parseSqlEnv

    ' ======================================================================
    ' 3. 预处理
    ' ======================================================================
    ' 1. 处理开头连续出现的 `)`
    ' 1.1. 调用方法处理 `)`
    Set parseSqlEnv = parseStartRightParenthesis(tokenScanner)

    ' 1.2. 如果 `result != null`，则说明开头是 `)`，立刻结束，并返回 `result`
    '         - 如果开头出现了 `)`，则表明出现了语法错误。这里忽略语法错误，并收集 `)` 为一个作用域
    If Not parseSqlEnv Is Nothing Then
        ' return result;
        Exit Function
    End If

    ' 2. 处理连续出现的 `(`
    Set leftParenthesisStack = parseStartLeftParenthesis(tokenScanner)

    ' 3. 检查: 如果扫描器已经用完
    If Not tokenScanner.hasNext Then
        ' 1. 将左括号栈转换成一个容器作用域
        Set parseSqlEnv = clearStackToContainerEnv(leftParenthesisStack)
        ' 2. 结束，并返回 `result`
        ' return result;
        Exit Function
    End If

    ' ======================================================================
    ' 4. 开始解析 sql
    ' ======================================================================
    Set parseSqlEnv = doParseSqlEnv(tokenScanner, leftParenthesisStack, needParseUnion)

    ' ======================================================================
    ' 5. 处理剩余的左括号
    ' ======================================================================
    ' 1. 检查: 如果左括号栈 `leftParenthesisStack` 不是空，则继续
    If Not UtilsStack.isEmptyObjStack(leftParenthesisStack) Then
        ' 2. 检查，如果 `result == null`
        If parseSqlEnv Is Nothing Then
            ' 2.1. 将左括号栈转换成一个容器作用域，调用 `result = clearStackToContainerEnv()`
            Set parseSqlEnv = clearStackToContainerEnv(leftParenthesisStack)

            ' 2.2. （因为解析失败了，所以在这里）强制吸收一个元素
            parseSqlEnv.addComponent tokenScanner.getNext

        ' 3. 否则
        Else
            ' 3.1. 将左括号栈吸收到作用域内部
            Set parseSqlEnv = consumeLeftParenthesisStackAllTokenToEnv(parseSqlEnv, leftParenthesisStack)
        End If
    End If

    ' ======================================================================
    ' 6. 释放左括号栈
    ' ======================================================================
    ' 1. 将左括号栈释放回池中
    UtilsPool.releaseObjStack leftParenthesisStack

    ' ======================================================================
    ' 7. 返回 `result`
    ' ======================================================================
    ' return result
End Function

' 解析sql并生成作用域--处理union
'
' @param tokenScanner token扫描器
' @param leftParenthesisStack （连续出现的）左括号栈 (ObjStack<SqlToken>)
' @param needParseUnion 是否需要解析 union
' @return 解析 sql 后生成的作用域
Private Function doParseSqlEnv( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef leftParenthesisStack As ObjStack, _
    ByRef needParseUnion As Boolean) As ISqlFormatterElem
    ' ==================================================
    ' 1. 生成一个基础作用域
    ' ==================================================
    ' 1. 生成一个基础作用域，保存到 `ISqlFormatterElem subEnv`
    '    不解析 union 部分
    Dim subEnv As ISqlFormatterElem
    Set subEnv = parseBaseEnv(tokenScanner, leftParenthesisStack)

    ' 2. 检查: 如果 `subEnv == null`，则退出，并返回 null
    '    表示发生了语法错误
    If subEnv Is Nothing Then
        ' return null
        Exit Function
    End If

    ' 3. 检查: 如果 `subEnv` 的作用域类型不是 `SqlTokenType.KEY_SELECT` 和 `SqlTokenType.KEY_UNION`，则结束，并返回 `subEnv`
    ' 如果基础作用域是带有边界括号的，那么有可能得到的是一个 union 作用域
    Dim envType As SqlTokenType
    envType = subEnv.getType
    If envType <> SqlTokenType.KEY_SELECT And envType <> SqlTokenType.KEY_UNION Then
        ' return subEnv
        Set doParseSqlEnv = subEnv
        Exit Function
    End If

    ' ==================================================
    ' 2. 如果不处理 union，则快速结束
    ' ==================================================
    ' 1. 检查: 如果 `needParseUnion == false` 并且不存在边界括号，则不需要解析到 union 关键字，则结束，并返回 `subEnv`
    If needParseUnion = False Then
        ' 检查: 如果不存在边界括号
        If UtilsStack.isEmptyObjStack(leftParenthesisStack) Then
            ' return subEnv
            Set doParseSqlEnv = subEnv
            Exit Function
        End If
    End If

    ' ==================================================
    ' 3. 处理 union
    ' ==================================================
    ' 1. 初始化结果作用域 `ISqlFormatterElem result = subEnv`
    '    这里直接使用 doParseSqlEnv
    Set doParseSqlEnv = subEnv

    Dim token As SqlToken
    ' 2. 遍历扫描器，如果扫描器已经用完，则退出遍历
    While tokenScanner.hasNext
        ' 3. 从扫描器中获取一个 `token`
        Set token = tokenScanner.peek

        ' 4. 检查: 如果 `token` 是注释
        If token.ISqlFormatterElem_isComment() Then
            ' 4.1. 将注释保存到作用域 `result`，并将结果重新保存到 `result`
            Set doParseSqlEnv = consumeTokenToEnvRight(doParseSqlEnv, token)
            ' 4.2. 扫描器向前推进
            tokenScanner.toNext
            ' 4.3. 进入下一次遍历
            GoTo whileContinue
        End If

        ' 5. 检查，如果 `token.tokenType != SqlTokenType.KEY_UNION`，则退出遍历
        If token.tokenType <> SqlTokenType.KEY_UNION Then
            GoTo whileBreak
        End If

        ' 6. 解析并吸收union开始的作用域，结果保存到 `result`
        Set doParseSqlEnv = parseFromUnionEnv(tokenScanner, leftParenthesisStack, doParseSqlEnv)

        ' 7. 检查: 如果 `needParseUnion == false` 并且已经不存在边界括号了，则不需要再解析 union 关键字，退出遍历
        If needParseUnion = False Then
            If UtilsStack.isEmptyObjStack(leftParenthesisStack) Then
                GoTo whileBreak
            End If
        End If
whileContinue:
    Wend
whileBreak:

    ' ==================================================
    ' 4. 返回 `result`
    ' ==================================================
    ' return result
End Function

' 解析并吸收union开始的作用域
'
' @param tokenScanner token扫描器
' @param leftParenthesisStack （连续出现的）左括号栈 (ObjStack<SqlToken>)
' @param startEnv 起始作用域
' @return 解析 sql 后生成的作用域
Public Function parseFromUnionEnv( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef leftParenthesisStack As ObjStack, _
    ByRef startEnv As ISqlFormatterElem) As ISqlFormatterElem

    ' 1. 声明变量
    ' 1.1. 结果作用域 `ISqlFormatterElem result = startEnv`
    '      这里直接使用 parseFromUnionEnv
    Set parseFromUnionEnv = startEnv
    ' 1.2. union 后的子作用域 `ISqlFormatterElem subEnv`
    Dim subEnv As ISqlFormatterElem
    ' 1.3 作用域类型 `SqlTokenType envType`
    Dim envType As SqlTokenType

    ' 2. 吸收 union 关键字到 union 作用域，并将结果重新保存到 `result`
    Set parseFromUnionEnv = mergeKeyUnion(tokenScanner, parseFromUnionEnv)

    ' 3. 吸收右侧连续出现的注释到作用域，结果保存到 `result`
    Set parseFromUnionEnv = consumeCommentToEnvRight(tokenScanner, parseFromUnionEnv)

    ' 4. 检查: 如果扫描器已经用完了，退出处理，并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If

    ' 5. 生成一个作用域 `subEnv`，不解析 union 部分
    '    - 这里不需要携带参数中的左括号栈，因为到这里已经相当于跨过了一些内容，完全不需要考虑开头的左括号
    Set subEnv = parseSqlEnv(tokenScanner, False)

    ' 6. 检查: 如果 `subEnv == null` (这里发生了语法异常)，退出处理，并返回 `result`
    If subEnv Is Nothing Then
        ' return result
        Exit Function
    End If

    ' 7. 检查: 如果作用域类型 `subEnv.getType() != SqlTokenType.KEY_SELECT && SqlTokenType.KEY_UNION` (发生了语法异常)
    envType = subEnv.getType
    If envType <> SqlTokenType.KEY_SELECT And envType <> SqlTokenType.KEY_UNION Then
        ' 7.1. 将 `result` + `subEnv` 合并到一个容器作用域中，结果保存到 `result`
        '      到这里虽然进行了合并，但是已经不是一个类型是 `KEY_UNION` 的作用域了
        Set parseFromUnionEnv = containEnvs(parseFromUnionEnv, subEnv)
    End If

    ' 8. 将 `subEnv` 保存到 `result`
    parseFromUnionEnv.addComponent subEnv

    ' 9. 对 `result` 做括号配对
    Set parseFromUnionEnv = matchEnvBoundaryParenthesis(parseFromUnionEnv, tokenScanner, leftParenthesisStack)

    ' 10. 退出处理，返回 `result`
    ' return result
End Function

' 解析sql并生成基础作用域--剥离with
'
' @param tokenScanner 扫描器
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @return 解析 sql 后生成的作用域
Public Function parseBaseEnv( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果作用域 `ISqlFormatterElem result`
    '    这里直接使用 parseBaseEnv
    ' 2. with 作用域 `withEnv`
    Dim withEnv As ISqlFormatterElem

    ' ==================================================
    ' 2. 遍历扫描器，处理开头的 with 作用域
    ' ==================================================
    Dim token As SqlToken
    ' 1. 如果扫描器已经用完，则退出遍历
    While tokenScanner.hasNext
        ' 2. 从 `tokenScanner` 中获取一个 `token`
        Set token = tokenScanner.peek

        ' 3. 检查: 如果 `token.tokenType == SqlTokenType.KEY_WITH`，生成一个 with 作用域
        If token.tokenType = SqlTokenType.KEY_WITH Then
            '    - 如果已经解析出了一个 `with` 作用域，则不需要再携带左括号栈了，因为那写左括号应该属于两个作用域合并之后的作用域
            ' 3.1. 检查: 如果 `with` 是 null
            If withEnv Is Nothing Then
                ' 3.1.1. 生成一个 with 作用域: `withEnv=parseWithEnv(withEnv, leftParenthesisStack)`
                Set withEnv = parseWithEnv(tokenScanner, leftParenthesisStack)
                ' 3.1.2. 检查: 如果 `withEnv` 是 null(解析失败)，则退出处理，并返回 null
                '        说明第一次解析 with 作用域时，可能发现了 with rollup，所以这里需要立刻终止后续的处理，并返回 null
                If withEnv Is Nothing Then
                    ' return null
                    Exit Function
                End If

            ' 3.2. 否则: 生成一个 with 作用域: `withEnv=parseWithEnv(withEnv, null)`
            '      如果已经解析出了一个 `with` 作用域，则不需要再携带左括号栈了，因为那写左括号应该属于两个作用域合并之后的作用域
            Else
                Set withEnv = parseWithEnv(tokenScanner, Nothing)
            End If

        ' 4. 继续检查: 如果 `token.tokenType == SqlTokenType.LEFT_PARENTHESIS`
        ElseIf token.tokenType = SqlTokenType.LEFT_PARENTHESIS Then
            ' 4.1. 生成一个作用域 `result`，**不解析 union 部分**
            Set parseBaseEnv = parseSqlEnv(tokenScanner, False)

            ' 4.2 检查: 如果 `result == null`，则立刻停止，并返回 `withEnv`
            '     - 到这里说明，`parseSqlEnv` 发生了解析异常，存在语法错误
            '     - 无论 `withEnv` 是不是 null，直接返回即可，因为在这之前只可能解析出了这一个作用域
            If parseBaseEnv Is Nothing Then
                ' return withEnv
                Set parseBaseEnv = withEnv
                Exit Function
            End If

            ' 4.3 否则，检查: 如果 `result.getType() == SqlTokenType.KEY_WITH`
            If parseBaseEnv.getType = SqlTokenType.KEY_WITH Then
                ' 4.3.1 合并 with 作用域
                Set withEnv = saveWithEnvGroup(withEnv, parseBaseEnv)
                ' 4.3.2. 对 `withEnv` 做括号配对
                Set withEnv = matchEnvBoundaryParenthesis(withEnv, tokenScanner, leftParenthesisStack)
                ' 3.3.2 重置: `result = null`
                Set parseBaseEnv = Nothing
            Else
                ' 3.4 否则，如果是其他类型的作用域，则退出当前遍历
                GoTo whileBreak
            End If

        ' 5. 否则，检查: 如果是 token 注释
        ElseIf token.ISqlFormatterElem_isComment Then
            ' 5.1. 将 token 保存到 `withEnv`
            Set withEnv = consumeTokenToEnvRight(withEnv, token)
            ' 5.2. 扫描器向前推进
            tokenScanner.toNext

        ' 6. 否则，如果是其他类型的 token，则退出当前遍历
        Else
            GoTo whileBreak
        End If
    Wend
whileBreak:

    ' ==================================================
    ' 3. 处理 with 之后的作用域
    ' ==================================================
    ' 1. 检查，如果: `result == null`，则继续
    '    正式生成一个作用域，如果 `result != null`，则在前面的遍历中已经生成了一个作用域
    If parseBaseEnv Is Nothing Then
        ' 2. 检查: 如果扫描器已经用完，则结束并返回 `withEnv`
        '    无论 `withEnv` 是不是 null，直接返回即可，因为在这之前只可能解析出了这一个作用域
        If Not tokenScanner.hasNext Then
            ' return withEnv
            Set parseBaseEnv = withEnv
            Exit Function
        End If

        ' 3. 尝试生成作用域，保存到 `result`
        If withEnv Is Nothing Then
            Set parseBaseEnv = doParseBaseEnv(tokenScanner, leftParenthesisStack)
        Else
            Set parseBaseEnv = doParseBaseEnv(tokenScanner, Nothing)
        End If

        ' 4. 检查: 如果 `result == null`，则立刻停止，并返回 `withEnv`
        '    到这里说明，存在语法错误
        '    无论 `withEnv` 是不是 null，直接返回即可，因为在这之前只可能解析出了这一个作用域
        If parseBaseEnv Is Nothing Then
            ' return withEnv
            Set parseBaseEnv = withEnv
            Exit Function
        End If
    End If

    ' ==================================================
    ' 4. 合并 `with` 和 `result` 作用域
    ' ==================================================
    ' 1. 检查，如果: `withEnv != null`，则继续
    If Not withEnv Is Nothing Then
        ' 2. 合并 `with` 和 `result` 作用域，结果保存到 `result`
        Set parseBaseEnv = mergeWithEnv(withEnv, parseBaseEnv)
    End If

    ' ==================================================
    ' 5. 对 `result` 做括号配对
    ' ==================================================
    Set parseBaseEnv = matchEnvBoundaryParenthesis(parseBaseEnv, tokenScanner, leftParenthesisStack)

    ' ==================================================
    ' 6. 返回 `result`
    ' ==================================================
    ' return result
End Function

' 生成几种基础类型的作用域
'
' @param tokenScanner 扫描器
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @return 作用域对象
Private Function doParseBaseEnv( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 doParseBaseEnv

    ' ==================================================
    ' 2. 按照 token 类型来解析
    ' ==================================================
    ' 1. 从 `tokenScanner` 中获取一个 `token`
    Dim token As SqlToken
    Set token = tokenScanner.peek

    ' 2. 检查: 如果 `token.tokenType == SqlTokenType.LEFT_PARENTHESIS`，调用 `parseSqlEnv(不解析 union)` 生成一个新的作用域，结果保存到 `result`
    If token.tokenType = SqlTokenType.LEFT_PARENTHESIS Then
        Set doParseBaseEnv = parseSqlEnv(tokenScanner, False)

    ' 3. 否则，如果 `token.tokenType == SqlTokenType.KEY_SELECT`，生成一个 `select` 作用域，结果保存到 `result`
    ElseIf token.tokenType = SqlTokenType.KEY_SELECT Then
        Set doParseBaseEnv = parseSelect(tokenScanner)

    ' 4. 否则，如果 `token` 类型是 `SqlTokenType.KEY_UPDATE`，生成一个 `update` 作用域，结果保存到 `result`
    ElseIf token.tokenType = SqlTokenType.KEY_UPDATE Then
        Set doParseBaseEnv = parseUpdate(tokenScanner)

    ' 5. 否则，如果 `token` 类型是 `SqlTokenType.KEY_DELETE`，生成一个 `delete` 作用域，结果保存到 `result`
    ElseIf token.tokenType = SqlTokenType.KEY_DELETE Then
        Set doParseBaseEnv = parseDelete(tokenScanner)

    ' 6. 否则，如果 `token` 类型是 `SqlTokenType.KEY_INSERT`，生成一个 `insert` 作用域，结果保存到 `result`
    ElseIf token.tokenType = SqlTokenType.KEY_INSERT Then
        Set doParseBaseEnv = parseInsert(tokenScanner)

    ' 7. 否则，（其他类型的 token），则退出处理，并返回 null
    Else
        ' return null
        Exit Function
    End If

    ' ==================================================
    ' 3. 匹配括号
    ' ==================================================
    ' 1. 执行: 列对象括号配对，结果保存到 `result`
    Set doParseBaseEnv = matchEnvBoundaryParenthesis(doParseBaseEnv, tokenScanner, leftParenthesisStack)

    ' ==================================================
    ' 4. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 解析完整的 with 作用域---外层逻辑 -- 进行括号配对
'
' @param tokenScanner 扫描器
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @return with 作用域
Public Function parseWithEnv( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem
    ' 1. 执行内层逻辑，解析并生成 with 作用域
    Dim result As ISqlFormatterElem
    Set parseWithEnv = doParseWithEnv(tokenScanner)

    ' 2. 检查，如果 `result == null`，则立刻结束，并返回 `null`
    If parseWithEnv Is Nothing Then
        ' return null;
        Exit Function
    End If

    ' 3. 对 `result` 做括号配对
    Set parseWithEnv = matchEnvBoundaryParenthesis(parseWithEnv, tokenScanner, leftParenthesisStack)

    ' return result
End Function

' 解析 with 语句
' 需要匹配的内容
'  withStatement -->  WITH [RECURSIVE] withTableEnv[, withTableEnv]
'
' @param tokenScanner 扫描器
' @return with 作用域
Private Function doParseWithEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 临时变量 `ISqlFormatterElem tempElem`
    Dim tempElem As ISqlFormatterElem
    ' 2. token `SqlToken token`
    Dim token As SqlToken

    ' ==================================================
    ' 2. 检查是不是 with 开头
    ' ==================================================
    ' 1. 从扫描器中获取一个 `token`
    Set token = tokenScanner.peek
    ' 2. 检查，如果 token 的类型 `!= KEY_WITH`，则结束并返回 null
    If token.tokenType <> SqlTokenType.KEY_WITH Then
        ' return null
        Exit Function
    End If
    ' 3. 标记扫描器: `mark` (防止后面遇到 `with rollup` 时，可以还原)
    tokenScanner.mark
    ' 4. 向前推进扫描器
    tokenScanner.toNext
    ' 5. 为 token 吸收一个同行注释并合并为TokenGroup，结果保存到 `tempElem`
    Set tempElem = consumeShareLineCommentToTokenGroup(token, tokenScanner)
    ' 6. 检查，如果扫描器已经用完了
    If Not tokenScanner.hasNext Then
        ' 6.1. 清除扫描器标记 `clearMark`
        tokenScanner.clearMark
        ' 6.2. 结束并返回 `tempElem`
        ' return tempElem
        Set doParseWithEnv = tempElem
        Exit Function
    End If

    ' ==================================================
    ' 3. 排除 `with rollup` 的情况
    ' ==================================================
    ' 1. 获取下一个 token 到 `token`: `peek`
    Set token = tokenScanner.peek
    ' 2. 检查: 如果 `token` 的类型是 rollup: `SqlTokenType.KEY_ROLLUP`
    If token.tokenType = SqlTokenType.KEY_ROLLUP Then
        ' 2.1. 重置扫描器位置并清除标记: `resetAndClearMark`
        tokenScanner.resetAndClearMark
        ' 2.2. 退出处理，并返回 null
        ' return null
        Exit Function

    ' 3. 否则，清除扫描器标记 `clearMark`
    Else
        tokenScanner.clearMark
    End If

    ' ==================================================
    ' 4. 初始化结果
    ' ==================================================
    ' 1. 创建结果对象 `SqlFormatterWithEnv result`
    Dim result As SqlFormatterWithEnv
    Set result = New SqlFormatterWithEnv
    ' 2. 将 tempElem 保存到 `result`
    result.ISqlFormatterElem_addComponent tempElem
    ' 3. 声明偏移量 `int offset`
    Dim offset As Integer

    ' ==================================================
    ' 5. 匹配 `[RECURSIVE]` + 注释
    ' ==================================================
    ' 1. 遍历扫描器
    ' 1.1 如果扫描器已经用完了，则退出遍历
    While tokenScanner.hasNext
        ' 1.2 从扫描器中获取一个 token
        Set token = tokenScanner.peek
        ' 1.3. 检查，如果 token 的类型 `== KEY_RECURSIVE`
        If token.tokenType = SqlTokenType.KEY_RECURSIVE Then
            ' 1.3.1. 将 token 作为组成元素保存到 `result`
            result.ISqlFormatterElem_addComponent token
            ' 1.3.2. 向前推进扫描器
            tokenScanner.toNext

        ' 1.4. 否则，如果是注释
        ElseIf SqlHelper.isCommentToken(token.tokenType) Then
            ' 1.4.1. 将 token 作为组成元素保存到 `result`
            result.ISqlFormatterElem_addComponent token
            ' 1.4.2. 向前推进扫描器
            tokenScanner.toNext

        ' 1.5. 否则，则退出遍历
        Else
            GoTo consumeRecusiveBreak
        End If
    Wend
consumeRecusiveBreak:

    ' 2. 检查，如果扫描器已经用完了，则结束并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Set doParseWithEnv = result
        Exit Function
    End If

    ' ==================================================
    ' 6. 匹配 `withTableEnv`
    ' ==================================================
    ' 1. 执行: 获取扫描器到下一个最近的with表定义的偏移量，结果保存到 `offset`
    offset = SqlFormatterHelper.getNextWithTableOffset(tokenScanner)
    ' 2. 检查: 如果不存在筛选项 `offset == 0`，退出处理，并返回 `result`
    If offset = 0 Then
        ' return result
        Set doParseWithEnv = result
        Exit Function

    ' 3. 否则，检查: 如果 `offset > 1`，执行: 吸收 offset - 1 个注释元素到 result 右侧
    ElseIf offset > 1 Then
        consumeComponentToEnvRight result, tokenScanner, offset - 1
    End If

    ' 4. 解析一个 with 表作用域，保存到 `tempElem`
    Set tempElem = parseWithTableEnv(tokenScanner)
    ' 5. 检查: 如果 `tempElem` 是空，则退出处理，并返回 `result`
    If tempElem Is Nothing Then
        ' return result
        Set doParseWithEnv = result
        Exit Function
    End If
    ' 6. 将 `tempElem` 保存到 `result`
    result.ISqlFormatterElem_addComponent tempElem

    ' ==================================================
    ' 7. 匹配 `[, withTableEnv]`
    ' ==================================================
    ' 7.1. 遍历扫描器, 如果扫描器已经用完了，则退出遍历
    While tokenScanner.hasNext
        ' ==============================
        ' 7.2. 处理逗号
        ' ==============================
        ' 1. 获取扫描器到下一个最近的逗号的偏移量，结果保存到 `offset`
        offset = SqlFormatterHelper.getNextCommaOffset(tokenScanner)
        ' 2. 检查: 如果不存在筛选项 `offset == 0`，退出遍历
        If offset = 0 Then
            GoTo consumeWithBreak

        ' 3. 否则，检查: 如果 `offset > 1`，执行: 吸收 offset - 1 个注释元素到 result 右侧
        ElseIf offset > 1 Then
            consumeComponentToEnvRight result, tokenScanner, offset - 1
        End If
        ' 4. 从扫描器中弹出一个 `token`，并保存到 `result`
        result.ISqlFormatterElem_addComponent tokenScanner.getNext

        ' ==============================
        ' 7.3. 匹配 `withTableEnv`
        ' ==============================
        ' 1. 执行: 获取扫描器到下一个最近的with表定义的偏移量，结果保存到 `offset`
        offset = SqlFormatterHelper.getNextWithTableOffset(tokenScanner)

        ' 2. 检查: 如果不存在筛选项 `offset == 0`，退出遍历
        If offset = 0 Then
            GoTo consumeWithBreak

        ' 3. 否则，检查: 如果 `offset > 1`，执行:吸收 offset - 1 个注释元素到 result 右侧
        ElseIf offset > 1 Then
            consumeComponentToEnvRight result, tokenScanner, offset - 1
        End If

        ' 4. 解析一个 with 表作用域，保存到 `tempElem`
        Set tempElem = parseWithTableEnv(tokenScanner)

        ' 5. 检查: 如果 `tempElem` 是空，则退出遍历
        If tempElem Is Nothing Then
            GoTo consumeWithBreak
        End If
        ' 6. 将 `tempElem` 保存到 `result`
        result.ISqlFormatterElem_addComponent tempElem

        ' ==============================
        ' 7.4. 从 2 开始进行下一轮遍历
        ' ==============================
    Wend
consumeWithBreak:

    ' ==================================================
    ' 8. 返回 `result`
    ' ==================================================
    ' return result
    Set doParseWithEnv = result
End Function

' 解析一个with表作用域
' 需要匹配的内容
'   withTableEnv --> cte_name [(col_name [, col_name] ...)] AS (subquery)
'
' @param tokenScanner 扫描器
' @return with 作用域
Public Function parseWithTableEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 解析 `cte_name` token
    ' ==================================================
    ' 1. 从扫描器中获取一个 token
    Dim token As SqlToken
    Set token = tokenScanner.peek

    ' 2. 检查，如果 token 的类型是 `STR_FUNC`，修改 token 的类型为 `STR_NAME`
    If token.tokenType = SqlTokenType.STR_FUNC Then
        token.tokenType = SqlTokenType.STR_NAME

    ' 3. 否则，检查，如果 token 的类型不是 `STR_NAME`，则结束并返回 null
    '    如果 with 后面不是一个函数名称，也不是一个普通字符串名称，
    '    则说明当前语法出现了问题，无法构成一个完整的 with 作用域
    ElseIf token.tokenType <> SqlTokenType.STR_NAME Then
        ' return null
        Exit Function
    End If
    ' 4. 将扫描器向后推进
    tokenScanner.toNext

    ' 5. 检查: 如果扫描器已经用完，则退出处理，并返回 `token`
    If Not tokenScanner.hasNext Then
        ' return token
        Set parseWithTableEnv = token
        Exit Function
    End If

    ' ==================================================
    ' 2. 创建作用域，保存 with 表名
    ' ==================================================
    ' 1. 创建一个结果对象
    Dim result As SqlFormatterWithTableEnv
    Set result = New SqlFormatterWithTableEnv
    ' 2. 使用 `cte_name` token 来初始化 `result`
    result.ISqlFormatterElem_addComponent token
    ' 3. 吸收右侧连续出现的注释，结果保存到 `result`
    Set result = consumeCommentToEnvRight(tokenScanner, result)
    ' 4. 检查，如果扫描器已经用完了，则结束并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Set parseWithTableEnv = result
        Exit Function
    End If

    ' ==================================================
    ' 3. 解析 `(col_name [, col_name] ...)`
    ' ==================================================
    ' 1. 尝试解析 with 表作用域中的列名
    Dim cols As ISqlFormatterElem
    Set cols = parseWithTableColNames(tokenScanner)

    ' 2. 检查，如果 `cols != null`，则将 `cols` 保存到 `result`
    If Not cols Is Nothing Then
        result.ISqlFormatterElem_addComponent cols
    End If

    ' 3. 吸收右侧连续出现的注释，结果保存到 `result`
    Set result = consumeCommentToEnvRight(tokenScanner, result)

    ' 4. 检查: 如果扫描器已经用完，则返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Set parseWithTableEnv = result
        Exit Function
    End If

    ' ==================================================
    ' 4. 解析 `as`
    ' ==================================================
    ' 1. 从扫描器中获取一个 token
    Set token = tokenScanner.peek

    ' 2. 检查如果 token 的类型 `!= KEY_AS`，则结束并返回 `result`
    If token.tokenType <> SqlTokenType.KEY_AS Then
        ' return result
        Set parseWithTableEnv = result
        Exit Function
    End If

    ' 3. 将 token 保存到 `result`
    result.ISqlFormatterElem_addComponent token

    ' 4. 向前推进扫描器
    tokenScanner.toNext

    ' 5. 吸收右侧连续出现的注释，结果保存到 `result`
    Set result = consumeCommentToEnvRight(tokenScanner, result)

    ' 6. 检查: 如果扫描器已经用完，则返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Set parseWithTableEnv = result
        Exit Function
    End If

    ' ==================================================
    ' 5. 解析 with 表的子查询内容
    ' ==================================================
    ' 1. 生成一个作用域 `temp`，不需要解析 union 部分
    Dim temp As ISqlFormatterElem
    Set temp = parseSqlEnv(tokenScanner, False)
    ' 2. 将 `temp` 保存到 `result` 中
    result.ISqlFormatterElem_addComponent temp

    ' ==================================================
    ' 6. 返回 `result`
    ' ==================================================
    ' return result
    Set parseWithTableEnv = result
End Function

' 解析with表作用域中的列名
' 需要匹配的内容
'    (col_name [, col_name] ...)
'
' @param tokenScanner 扫描器
' @return with 作用域
Public Function parseWithTableColNames(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 处理第一个 `(`
    ' ==================================================
    ' 1. 从扫描器中获取一个 token
    Dim token As SqlToken
    Set token = tokenScanner.peek

    Dim tokenType As SqlTokenType

    ' 2. 检查，如果 token 的类型 `!= LEFT_PARENTHESIS`，则结束并返回 null
    If token.tokenType <> SqlTokenType.LEFT_PARENTHESIS Then
        ' return null
        Exit Function
    End If
    ' 3. 向前推进扫描器
    tokenScanner.toNext
    ' 4. 检查: 如果扫描器已经用完了，则退出处理，并返回 `token`
    If Not tokenScanner.hasNext Then
        ' return token
        Set parseWithTableColNames = token
        Exit Function
    End If

    ' ==================================================
    ' 2. 初始化结果
    ' ==================================================
    ' 1. 创建一个结果对象 `SqlFormatterCommaDeliMultiElem result`
    Dim result As SqlFormatterCommaDeliMultiElem
    Set result = New SqlFormatterCommaDeliMultiElem
    ' 2. 增加左边界括号的数量
    result.ISqlFormatterElem_addBoundaryLeftParenthesis
    ' 3. 记录未匹配的 `(` 的数量
    Dim unmatchedParenthesisCount As Integer
    unmatchedParenthesisCount = 1

    ' ==================================================
    ' 3. 尝试吸收连续出现的 `(`
    ' ==================================================
    ' 1. 遍历扫描器，如果扫描器已经用完了，则退出遍历
    While tokenScanner.hasNext
        ' 2. 获取一个 token
        Set token = tokenScanner.peek

        ' 3. 检查: 如果 token 的类型 `== LEFT_PARENTHESIS`
        If token.tokenType = SqlTokenType.LEFT_PARENTHESIS Then
            ' 3.1. 累计未配对的 `(` 的数量 `unmatchedParenthesisCount++`
            unmatchedParenthesisCount = unmatchedParenthesisCount + 1
            ' 3.2. 增加左边界括号的数量
            result.ISqlFormatterElem_addBoundaryLeftParenthesis

        ' 4. 否则，则退出当前遍历
        Else
            GoTo consumeLeftParenthesisBreak
        End If

        ' 5. 向前推进扫描器
        tokenScanner.toNext
        ' 6. 进入下一次遍历
    Wend
consumeLeftParenthesisBreak:

    ' ==================================================
    ' 4. 尝试吸收连续出现的注释
    ' ==================================================
    ' 1. 遍历扫描器，如果扫描器已经用完了，则退出遍历
    While tokenScanner.hasNext
        ' 2. 获取一个 token
        Set token = tokenScanner.peek
        ' 3. 检查: 如果是注释，将 token 保存到 `result`
        If token.ISqlFormatterElem_isComment Then
            result.ISqlFormatterElem_addComponent token

        ' 4. 否则，则退出当前遍历
        Else
            GoTo consumeCommentBreak
        End If

        ' 5. 向前推进扫描器
        tokenScanner.toNext
        ' 6. 进入下一次遍历
    Wend
consumeCommentBreak:

    ' ==================================================
    ' 5. 匹配一个 `col_name`
    ' ==================================================
    ' 1. 扫描器检查: 如果扫描器已经用完了，则结束并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Set parseWithTableColNames = result
        Exit Function
    End If

    ' 2. 从扫描器中获取一个 token
    Set token = tokenScanner.peek
    tokenType = token.tokenType

    ' 3. 检查，如果 `tokentokeType == STR_NAME`
    If tokenType = SqlTokenType.STR_NAME Then
        ' 3.1. 向前推进扫描器
        tokenScanner.toNext
        ' 3.2. 将 token 保存到 `result`
        result.ISqlFormatterElem_addComponent token
    End If
    ' 4. 否则 (!= STR_NAME)
    '  - 到这里是发生了语法异常，这里为了兼容异常不中断，并继续处理

    ' ==================================================
    ' 6. 匹配剩余的 `[, col_name]` 部分
    ' ==================================================
    ' 1. 初始化: 是否发现了逗号: `boolean hasComma = false`
    Dim hasComma As boolean

    ' 2. 遍历扫描器, 如果扫描器已经用完了，则退出遍历
    While tokenScanner.hasNext
        ' 3. 从扫描器中获取一个 token，并获取类型 `tokenType`
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' 4. 检查: 如果 `tokenType == COMMA`
        If tokenType = SqlTokenType.COMMA Then
            ' 4.1. 将 token 保存到 `result`
            result.ISqlFormatterElem_addComponent token
            ' 4.2. 向前推进扫描器
            tokenScanner.toNext
            ' 4.3. 设置 `hasComma = true`
            hasComma = True

        ' 5. 否则，检查: 如果类型是注释: `SqlHelper.isCommentToken`
        ElseIf SqlHelper.isCommentToken(tokenType) Then
            ' 5.1. 将 token 保存到 `result`
            result.ISqlFormatterElem_addComponent token
            ' 5.2. 向前推进扫描器
            tokenScanner.toNext

        ' 6. 否则，检查: 如果 `hasComma == false`，则退出遍历
        ElseIf hasComma = False Then
            GoTo consumeColBreak

        ' 7. 否则，检查: 如果 `tokenType == STR_NAME`
        ElseIf tokenType = SqlTokenType.STR_NAME Then
            ' 7.1. 将 token 保存到 `result`
            result.ISqlFormatterElem_addComponent token
            ' 7.2. 向前推进扫描器
            tokenScanner.toNext
            ' 7.3. 重制 `hasComma = false`
            hasComma = False

        ' 8. 否则，退出当前遍历
        Else
            GoTo consumeColBreak
        End If

        ' 9. 从 3 开始下一次遍历
    Wend
consumeColBreak:

    ' ==================================================
    ' 7. 扫描器检查: 如果扫描器已经用完了，则结束并返回 `result`
    ' ==================================================
    If Not tokenScanner.hasNext Then
        ' return result
        Set parseWithTableColNames = result
        Exit Function
    End If

    ' ==================================================
    ' 8. 退出 `)`
    ' ==================================================
    ' 1. 遍历扫描器，如果 `unmatchedParenthesisCount == 0` 或者扫描器已经用完了，则退出遍历
    While unmatchedParenthesisCount <> 0 And tokenScanner.hasNext
        ' 2. 从扫描器中获取一个 token
        Set token = tokenScanner.peek

        ' 3. 否则，检查，如果 `tokenType == RIGHT_PARENTHESIS`
        If token.tokenType = SqlTokenType.RIGHT_PARENTHESIS Then
            ' 3.1 完成一个 `(` 配对，`unmatchedParenthesisCount--`
            unmatchedParenthesisCount = unmatchedParenthesisCount - 1
            ' 3.2. 增加右边界括号的数量
            result.ISqlFormatterElem_addBoundaryRightParenthesis

        ' 4. 否则，退出遍历
        Else
            GoTo consumeRightParenthesisBreak
        End If
        ' 5. 向前推进扫描器
        tokenScanner.toNext
        ' 6. 然后回到 1 继续
    Wend
consumeRightParenthesisBreak:

    ' ==================================================
    ' 9. 结束并返回 `result`
    ' ==================================================
    Set parseWithTableColNames = result
End Function

' 各类型sql的组成部分的通用解析
'
' @param tokenScanner 扫描器
' @return select 作用域
Public Function parseSqlComposition( _
    ByRef subEnvAnalyzer As ISqlFormatterSqlSubEnvAnalyzer, _
    ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果作用域
    Dim result As SqlFormatterContainerEnv
    ' 2. 临时作用域
    Dim temp As ISqlFormatterElem
    ' 3. 外层左括号栈
    Dim outerLeftParenthesisStack As ObjStack
    ' 4. 内层左括号栈
    Dim innerLeftParenthesisStack As ObjStack
    ' 5. 容器作用域
    Dim containerSubEnv As SqlFormatterContainerEnv

    ' ==================================================
    ' 2. 解析当前 sql 开始部分的作用域
    ' ==================================================
    ' 1. 生成开始部分的作用域，并保存到 `temp`
    Set temp = subEnvAnalyzer.parseStartCompositionEnv(tokenScanner)

    ' 2. 检查: 如果 `temp == null`，则结束，并返回 null
    If temp Is Nothing Then
        ' return null
        Exit Function
    End If

    ' 3. 创建结果对象 `result = new SqlFormatterContainerEnv`，
    '    设置作用类型为 `ISqlFormatterSqlSubEnvAnalyzer#getType`，并保存 `temp`
    Set result = New SqlFormatterContainerEnv
    result.setType subEnvAnalyzer.getType
    result.ISqlFormatterElem_addComponent temp

    ' 4. 检查，如果扫描器已经用完，则结束，并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Set parseSqlComposition = result
        Exit Function
    End If

    ' ==================================================
    ' 3. 遍历，并收集当前 sql 的其他作用域
    ' ==================================================
    Dim token As SqlToken
    Dim tokenType As SqlTokenType

    ' ==============================
    ' 3.1. 如果扫描器用完了，则退出遍历
    ' ==============================
    While tokenScanner.hasNext
        ' ==============================
        ' 3.2. 从扫描器中获取一个 `token`，并保存 `tokenType = token.tokenType`
        ' ==============================
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' ==============================
        ' 3.3. 检查: 如果 `SqlTokenType.KEY_UNION`，则退出遍历
        ' ==============================
        If tokenType = SqlTokenType.KEY_UNION Then
            GoTo whileBreak
        End If

        ' ==============================
        ' 3.4. 处理右括号
        ' ==============================
        ' 1. 检查: 如果 `tokenType == SqlTokenType.RIGHT_PARENTHESIS`，则继续
        If tokenType = SqlTokenType.RIGHT_PARENTHESIS Then
            ' 2. 检查：如果 `outerLeftParenthesisStack != null`
            If Not outerLeftParenthesisStack Is Nothing Then
                ' 2.1. 对 `containerSubEnv` 做: 括号配对，尽量吸收 `outerLeftParenthesisStack`
                '     - 到这里，说明出现了语法异常
                Set containerSubEnv = matchEnvBoundaryParenthesis(containerSubEnv, tokenScanner, outerLeftParenthesisStack)
                ' 2.2. 吸收多余的左括号栈到作用域左侧，防止`outerLeftParenthesisStack` 出现剩余的左括号
                Set containerSubEnv = consumeLeftParenthesisStackAllTokenToEnv(containerSubEnv, outerLeftParenthesisStack)
                ' 2.3. 将 `containerSubEnv` 添加到 `result`
                result.ISqlFormatterElem_addComponent containerSubEnv
                ' 2.4. 将 `outerLeftParenthesisStack` 回收到池中
                UtilsPool.releaseObjStack outerLeftParenthesisStack
                ' 2.5. 重制 `containerSubEnv = null`, `outerLeftParenthesisStack = null`
                Set containerSubEnv = Nothing
                Set outerLeftParenthesisStack = Nothing
                ' 2.6. 检查，如果扫描器用完，则退出遍历
                If Not tokenScanner.hasNext Then
                    GoTo whileBreak
                End If
                ' 2.7. 否则，从扫描器中获取一个 `token`，并保存 `tokenType = token.tokenType`
                Set token = tokenScanner.peek
                tokenType = token.tokenType

            ' 3. 否则，退出遍历
            Else
                ' - 这里说明已经到达了整个 select 的边界括号的外侧
                GoTo whileBreak
            End If
        End If

        ' ==============================
        ' 3.5. 处理连续出现的注释
        ' ==============================
        ' 1. 检查: 如果 token 是注释: `SqlHelper.isCommentToken`，则继续
        If SqlHelper.isCommentToken(tokenType) Then
            ' 2. 吸收右侧连续出现的注释到作用域
            '    如果 `containerSubEnv != null`，则吸收到 `containerSubEnv`，否则吸收到 `result`
            If Not containerSubEnv Is Nothing Then
                Set containerSubEnv = consumeCommentToEnvRight(tokenScanner, containerSubEnv)
            Else
                Set result = consumeCommentToEnvRight(tokenScanner, result)
            End If

            ' 3. 检查，如果扫描器用完，则退出遍历
            If Not tokenScanner.hasNext Then
                GoTo whileBreak
            End If

            ' 4. 否则，从扫描器中获取一个 `token`，并保存 `tokenType = token.tokenType`
            Set token = tokenScanner.peek
            tokenType = token.tokenType
        End If

        ' ==============================
        ' 3.6. 处理左括号
        ' ==============================
        ' 1. 检查: 如果 `tokenType == SqlTokenType.LEFT_PARENTHESIS`，则继续
        If tokenType = SqlTokenType.LEFT_PARENTHESIS Then
            ' 2. 解析左括号，并保存到 `innerLeftParenthesisStack`
            Set innerLeftParenthesisStack = parseStartLeftParenthesis(tokenScanner)
            ' 3. 检查: 如果 `innerLeftParenthesisStack != null`
            If Not innerLeftParenthesisStack Is Nothing Then
                ' 3.1. 检查: 如果扫描器用完
                If Not tokenScanner.hasNext Then
                    ' 3.1.1 导出左括号栈到作用域: 将栈中的所有内容保存到作用域末尾
                    '       - 如果 `containerSubEnv != null`，则吸收到 `containerSubEnv`，否则吸收到 `result`
                    If Not containerSubEnv Is Nothing Then
                        Set containerSubEnv = appendStackToEnv(containerSubEnv, innerLeftParenthesisStack)
                    Else
                        Set result = appendStackToEnv(result, innerLeftParenthesisStack)
                    End If

                    ' 3.1.2. 将 `innerLeftParenthesisStack` 回收到池中
                    UtilsPool.releaseObjStack innerLeftParenthesisStack
                    ' 3.1.3. 重置 `innerLeftParenthesisStack = null`
                    Set innerLeftParenthesisStack = Nothing
                    ' 3.1.4. 退出遍历
                    GoTo whileBreak

                ' 3.2. 否则，从扫描器中获取一个 `token`，并保存 `tokenType = token.tokenType`
                Else
                    Set token = tokenScanner.peek
                    tokenType = token.tokenType
                End If
            End If
        End If

        ' ==============================
        ' 3.7. 尝试匹配其他作用域
        ' ==============================
        ' 1. 生成 sql 其他组成部分的作用域，结果保存到 `temp`
        Set temp = subEnvAnalyzer.parseSubCompositionEnv( _
            tokenScanner, innerLeftParenthesisStack, tokenType)

        ' 2. 检查: 如果解析失败 `temp == null`
        If temp Is Nothing Then
            ' 2.1. 将 `innerLeftParenthesisStack` 中的所有内容退回到扫描器中
            putBackStackToScanner tokenScanner, innerLeftParenthesisStack

            ' 2.2. 退出遍历
            GoTo whileBreak
        End If

        ' 3. 否则，检查: 如果 innerLeftParenthesisStack 内有元素
        If UtilsStack.isEmptyObjStack(innerLeftParenthesisStack) = False Then
            ' 3.1. 检查: 如果 `outerLeftParenthesisStack == null`
            If outerLeftParenthesisStack Is Nothing Then
                ' 3.1.1. 设置 `outerLeftParenthesisStack = innerLeftParenthesisStack`（括号升级）
                Set outerLeftParenthesisStack = innerLeftParenthesisStack
                ' 3.1.2. 创建新的 `containerSubEnv`
                Set containerSubEnv = New SqlFormatterContainerEnv

            ' 3.2. 否则，吸收所有左括号
            Else
                ' 3.2.1 吸收左括号栈 innerLeftParenthesisStack 到作用域 temp 左侧
                Set temp = consumeLeftParenthesisStackAllTokenToEnv(temp, innerLeftParenthesisStack)
                ' 3.2.2. 将 `innerLeftParenthesisStack` 回收到池中
                UtilsPool.releaseObjStack innerLeftParenthesisStack
            End If

            ' 3.3. 重置 `innerLeftParenthesisStack = null`
            Set innerLeftParenthesisStack = Nothing
        End If

        ' 4. 保存 `temp`
        ' 4.1. 检查，如果 `containerSubEnv != null`，则将 `temp` 保存到 containerSubEnv
        If Not containerSubEnv Is Nothing Then
            containerSubEnv.ISqlFormatterElem_addComponent temp

        ' 4.2. 否则，将 `temp` 保存到 `result`
        Else
            result.ISqlFormatterElem_addComponent temp
        End If
    Wend
whileBreak:

    ' ==================================================
    ' 4. 检查：如果 `outerLeftParenthesisStack != null` (语法异常，没有配对就停止了)
    ' ==================================================
    If Not outerLeftParenthesisStack Is Nothing Then
        ' 1. 吸收多余的左括号栈到作用域左侧 `containerSubEnv` ，防止出现剩余的左括号
        Set containerSubEnv = consumeLeftParenthesisStackAllTokenToEnv(containerSubEnv, outerLeftParenthesisStack)

        ' 2. 将 `containerSubEnv` 添加到 `result`
        '    `containerSubEnv` 只会在出现 `outerLeftParenthesisStack` 时，才会创建，所以不需要再在后面单独判断是否存在
        result.ISqlFormatterElem_addComponent containerSubEnv

        ' 3. 将 `outerLeftParenthesisStack` 回收到池中
        UtilsPool.releaseObjStack outerLeftParenthesisStack
    End If

    ' ==================================================
    ' 6. 返回 `result`
    ' ==================================================
    Set parseSqlComposition = result
End Function

' 解析 select sql片段，并生成作用域
' 需要匹配的内容
'     subSelect --> (subSelect)
'     subSelectStatement -->
'        [with...] select [selectAction] selectItems
'        [from tableInfo]
'        [[left[outer]|right[outer]|inner]join tableInfo [condition]]
'        [where conditions]
'        [having col]
'        [group by col]
'        [order by col]
'        [limit .. offset ... ]
'
' @param tokenScanner 扫描器
' @return select 作用域
Public Function parseSelect(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' 1. 获取 select sql 组成部分的解析器: `getSelectAnalyzer`
    ' 2. 执行: 各类型sql的组成部分的通用解析流程，并返回执行结果
    Set parseSelect = parseSqlComposition(getSelectAnalyzer, tokenScanner)
End Function

' 解析select关键字及其筛选项
'
' @param tokenScanner 扫描器
' @return 包含 select 关键字和筛选项的 SqlFormatterKeySectionEnv 作用域对象
Public Function parseSelectItemsEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 筛选项: `ISqlFormatterElem elem = null`
    Dim elem As ISqlFormatterElem
    ' 2. token `SqlToken token`
    Dim token As SqlToken

    ' ==================================================
    ' 2. 初始化目标作用域
    ' ==================================================
    ' 1. 从扫描器中弹出一个 `SqlToken token`
    Set token = tokenScanner.getNext
    ' 2. 为token吸收一个同行注释并合并为TokenGroup，结果保存到 `elem`
    Set elem = consumeShareLineCommentToTokenGroup(token, tokenScanner)
    ' 3. 检查: 如果扫描器已经用完，则结束并返回 `elem`
    If Not tokenScanner.hasNext Then
        ' return elem
        Set parseSelectItemsEnv = elem
        Exit Function
    End If
    ' 4. 创建作用域 `SqlFormatterKeySectionEnv result`，并使用 `elem` 和 `SqlTokenType.KEY_SELECT` 初始化
    Dim result As SqlFormatterKeySectionEnv
    Set result = New SqlFormatterKeySectionEnv
    result.Init elem, SqlTokenType.KEY_SELECT

    ' ==================================================
    ' 3. 初始化变量
    ' ==================================================
    ' 1. tokenType `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType
    ' 2. 筛选项保存对象: `ISqlFormatterElem itemContainer = null`
    Dim itemContainer As ISqlFormatterElem
    ' 3. 偏移量 `int offset`
    Dim offset As Integer

    ' ==================================================
    ' 4. 吸收第一个列元素之前的内容: 注释或者 select 的筛选行为关键字
    ' ==================================================
    ' 1. 开始遍历，直到扫描器用完
    While tokenScanner.hasNext
        ' 2. 执行: 获取扫描器到下一个最近的筛选行为关键字的偏移量，结果保存到 `offset`
        offset = SqlFormatterHelper.getNextSelectStartActionSqlKeyOffset(tokenScanner)
        ' 3. 检查: 如果 `offset == 0`，则退出遍历
        If offset = 0 Then
            Goto consumeActionKeyBreak

        ' 4. 否则，检查: 如果 `offset > 1`，执行:[吸收 offset - 1 个注释元素到 result 右侧](#吸收多个元素到作用域右侧)
        ElseIf offset > 1 Then
            consumeComponentToEnvRight result, tokenScanner, offset - 1
        End If
        ' 5. 从扫描器中弹出一个 `token` (这个是筛选行为关键字)
        Set token = tokenScanner.getNext
        ' 6. 将 `token` 保存到 `result`
        result.ISqlFormatterElem_addComponent token
        ' 7. 执行: 吸收一个同行注释到 result 右侧
        consumeOneShareLineCommentToEnvRight result, tokenScanner
        ' 8. 进行下一轮遍历
    Wend
consumeActionKeyBreak:

    ' ==================================================
    ' 5. 吸收列开始前的注释
    ' ==================================================
    ' 1. 执行: 获取扫描器到下一个最近的不是使匹配筛选项操作停止的sql关键字的偏移量，结果保存到 `offset`
    offset = SqlFormatterHelper.getNextNonSelectItemStopSqlKeyOffset(tokenScanner)
    ' 2. 检查: 如果不存在筛选项 `offset == 0`
    If offset = 0 Then
        ' 2.1. 对 `result` 执行: 吸收一个同行注释到作用域右侧，结果保存到 `result`
        Set result = consumeOneShareLineCommentToEnvRight(result, tokenScanner)
        ' 2.2. 退出处理，并返回 `result`
        ' return result
        Set parseSelectItemsEnv = result
        Exit Function

    ' 3. 否则，检查: 如果 `offset > 1`
    ElseIf offset > 1 Then
        ' 3.1. 执行: 吸收 offset - 1 个注释元素到 result 右侧，结果保存到 `result`
        Set result = consumeComponentToEnvRight(result, tokenScanner, offset - 1)
    End If

    ' ==================================================
    ' 6. 解析第一个筛选项
    ' ==================================================
    ' 1. 检查: 如果扫描器已经用完，则退出处理，返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Set parseSelectItemsEnv = result
        Exit Function
    End If
    ' 2. 执行: 解析select筛选项，结果保存到 `elem`
    Set elem = parseSelectItemInfo(tokenScanner)
    ' 3. 检查: 如果 `elem == null`，则退出处理，并返回 `result`
    If elem Is Nothing Then
        ' return result
        Set parseSelectItemsEnv = result
        Exit Function
    End If
    ' 4. 创建筛选项保存对象: `itemContainer`: `SqlFormatterHelper.createCommaDelimitMultiElem`
    Set itemContainer = SqlFormatterHelper.createCommaDelimitMultiElem(True)
    ' 5. 将 `elem` 保存到 `itemContainer`
    itemContainer.addComponent elem
    ' 6. 将 `itemContainer` 保存到 `result`
    result.ISqlFormatterElem_addComponent itemContainer

    ' ==================================================
    ' 7. 收集筛选项
    ' ==================================================
    ' 7.1. 遍历扫描器，直到扫描器用完
    While tokenScanner.hasNext
        ' ==============================
        ' 7.2. 吸收逗号
        ' ==============================
        ' 1. 执行: 获取扫描器到下一个最近的逗号的偏移量，结果保存到 `offset`
        offset = SqlFormatterHelper.getNextCommaOffset(tokenScanner)
        ' 2. 检查: 如果 `offset == 0`，则退出遍历
        If offset = 0 Then
            GoTo whileBreak

        ' 3. 否则，检查: 如果 `offset > 1`
        ElseIf offset > 1 Then
            ' 3.1. 执行: 吸收 offset - 1 个注释元素到 itemContainer 右侧，结果保存到 `itemContainer`
            Set itemContainer = consumeComponentToEnvRight(itemContainer, tokenScanner, offset - 1)
        End If

        ' 4. 从扫描器中弹出一个 `token` (这个是逗号)
        Set token = tokenScanner.getNext

        ' 5. 将 `token` 保存到 `itemContainer`
        itemContainer.addComponent token

        ' ==============================
        ' 7.2. 吸收列
        ' ==============================
        ' 1. 执行: 获取扫描器到下一个最近的不是使匹配筛选项操作停止的sql关键字的偏移量，结果保存到 `offset`
        offset = SqlFormatterHelper.getNextNonSelectItemStopSqlKeyOffset(tokenScanner)
        ' 2. 检查: 如果 `offset == 0`，则退出遍历
        If offset = 0 Then
            GoTo whileBreak

        ' 3. 检查: 如果 `offset > 1`
        ElseIf offset > 1 Then
            ' 3.1. 执行: 吸收 offset - 1 个注释元素到 itemContainer 右侧，结果保存到 `itemContainer`
            Set itemContainer = consumeComponentToEnvRight(itemContainer, tokenScanner, offset - 1)
        End If
        ' 4. 执行: 解析select筛选项，结果保存到 `elem`
        Set elem = parseSelectItemInfo(tokenScanner)

        ' 5. 检查: 如果 `elem == null`，退出遍历
        If elem Is Nothing Then
            GoTo whileBreak
        End If

        ' 6. 将 `elem` 保存到 `itemContainer`
        itemContainer.addComponent elem

        ' ==============================
        ' 7.3. 进入下一轮遍历
        ' ==============================
    Wend
whileBreak:

    ' ==================================================
    ' 8. 吸收注释
    ' ==================================================
    ' 1. 对 `result` 执行: 吸收一个同行注释到作用域右侧，结果保存到 `result`
    Set result = consumeOneShareLineCommentToEnvRight(result, tokenScanner)

    ' ==================================================
    ' 9. 退出处理，并返回 `result`
    ' ==================================================
    Set parseSelectItemsEnv = result
End Function

' 处理 select 筛选项
' 结束当前匹配时，一定会定位到一个最近的结束标记
'
' @param tokenScanner 扫描器
' @return 筛选项列对象
Public Function parseSelectItemInfo(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 解析一个筛选项
    ' ==================================================
    ' 1. 匹配一个筛选项 `result`
    Set parseSelectItemInfo = parseMixedColExpr(tokenScanner, false)

    ' 2. 检查: 如果 `result == null`，则结束，并返回 null
    '    这里说明出现了语法异常
    If parseSelectItemInfo Is Nothing Then
        ' return null;
        Exit Function
    End If

    ' ==================================================
    ' 2. 处理别名
    ' ==================================================
    ' 1. 执行: 吸收 result 的别名，结果保存到 `result`
    Set parseSelectItemInfo = parseColAliasName(tokenScanner, parseSelectItemInfo)

    ' ==================================================
    ' 3. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 吸收列后面的别名
'
' @param tokenScanner 扫描器
' @param col 列对象
' @return 吸收别名后的列对象
Public Function parseColAliasName( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef col As ISqlFormatterElem) As ISqlFormatterElem

    ' ==================================================
    ' 1. 初始化结果对象 `ISqlFormatterElem result = col`
    ' ==================================================
    ' 这里直接使用 parseColAliasName
    Set parseColAliasName = col

    ' ==================================================
    ' 2. 检查: 如果扫描器没有用完，则获取一个`token`。否则，退出，并返回 result
    ' ==================================================
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If

    Dim token As SqlToken
    Set token = tokenScanner.peek

    ' ==================================================
    ' 3. 处理只有别名，没有 As 的情况
    ' ==================================================
    ' 1. 检查: 如果 `token` 的类型是 `SqlTokenType.STR_NAME`，则继续
    If token.tokenType = SqlTokenType.STR_NAME Or token.tokenType = SqlTokenType.STR_STR Then
        ' 2. 检查: 如果 `result` 的类型不是 `SqlFormatterContainerCol`，则将 `result` 包装为 `SqlFormatterContainerCol`，结果保存到 `result`
        If Not Typeof parseColAliasName Is SqlFormatterContainerCol Then
            Set parseColAliasName = SqlFormatterHelper.wrapperColToContainerCol(parseColAliasName)
        End If
        ' 3. 将 `token` 保存到 `result`
        parseColAliasName.addComponent token
        ' 4. 扫描器向前推进
        tokenScanner.toNext
        ' 5. 退出处理，返回 `result`
        ' return result
        Exit Function
    End If

    ' ==================================================
    ' 4. 处理 `As 别名` 的情况中的 `As`
    ' ==================================================
    ' 1. 检查: 如果 `token` 的类型不是 `SqlTokenType.KEY_AS`，则退出处理，并返回 `result`
    If token.tokenType <> SqlTokenType.KEY_AS Then
        ' return result
        Exit Function
    End If

    ' 2. 检查: 如果 `result` 的类型不是 `SqlFormatterContainerCol`，则将 `result` 包装为 `SqlFormatterContainerCol`，结果保存到 `result`
    If Not Typeof parseColAliasName Is SqlFormatterContainerCol Then
        Set parseColAliasName = SqlFormatterHelper.wrapperColToContainerCol(parseColAliasName)
    End If

    ' 3. 将 `token` 保存到 `result`
    parseColAliasName.addComponent token
    ' 4. 扫描器向前推进
    tokenScanner.toNext
    ' 5. 检查: 如果扫描器已经用完，则退出处理
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If

    ' ==================================================
    ' 5. 处理 `As 别名` 的情况中的 `别名`
    ' ==================================================
    ' 1. 从扫描器中获取一个 `token`
    Set token = tokenScanner.peek

    ' 2. 检查: 如果 `token` 的类型是 `SqlTokenType.STR_NAME` 或者 `SqlTokenType.STR_STR`，则继续
    If token.tokenType = SqlTokenType.STR_NAME Or token.tokenType = SqlTokenType.STR_STR Then
        ' 3. 将 `token` 保存到 `result`
        parseColAliasName.addComponent token

        ' 4. 扫描器向前推进
        tokenScanner.toNext
    End If

    ' ==================================================
    ' 6. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 解析 select sql片段，并生成作用域
'
' @param tokenScanner 扫描器
' @return select 作用域
Public Function parseUpdate(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' 1. 获取 update sql 组成部分的解析器: `getUpdateAnalyzer`
    ' 2. 执行: 各类型sql的组成部分的通用解析流程，并返回执行结果
    Set parseUpdate = parseSqlComposition(getUpdateAnalyzer, tokenScanner)
End Function

' 解析update关键字及其下的所有表定义
'
' @param tokenScanner 扫描器
' @return 包含 update 关键字和表定义的 SqlFormatterKeySectionEnv 作用域对象
Public Function parseUpdateTableReferencesEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 筛选项: `ISqlFormatterElem elem = null`
    Dim elem As ISqlFormatterElem

    ' ==================================================
    ' 2. 初始化目标作用域
    ' ==================================================
    ' 1. 从扫描器中弹出一个 token 保存到 `elem`
    Set elem = tokenScanner.getNext
    ' 2. 为 elem 吸收一个同行注释并合并为TokenGroup，结果保存到 `elem`
    Set elem = consumeShareLineCommentToTokenGroup(elem, tokenScanner)
    ' 3. 检查: 如果扫描器已经用完，则结束并返回 `elem`
    If Not tokenScanner.hasNext Then
        ' return elem
        Set parseUpdateTableReferencesEnv = elem
        Exit Function
    End If

    ' 4. 创建作用域 `SqlFormatterKeySectionEnv result`，并使用 `elem` 和 `SqlTokenType.KEY_UPDATE` 初始化
    Dim result As SqlFormatterKeySectionEnv
    Set result = New SqlFormatterKeySectionEnv
    result.Init elem, SqlTokenType.KEY_UPDATE

    ' ==================================================
    ' 3. 初始化变量
    ' ==================================================
    ' 1. 偏移量 `int offset`
    Dim offset As Integer

    ' ==================================================
    ' 4. 吸收第一个表定义之前的内容: 注释或者 update 的行为关键字
    ' ==================================================
    ' 1. 开始遍历，直到扫描器用完
    While tokenScanner.hasNext
        ' 2. 执行: 获取扫描器到下一个最近的update行为关键字的偏移量，结果保存到 `offset`
        offset = SqlFormatterHelper.getNextUpdateStartActionSqlKeyOffset(tokenScanner)
        ' 3. 检查: 如果 `offset == 0`，则退出遍历
        If offset = 0 Then
            GoTo whileBreak
        End If

        ' 4. 执行: 吸收 offset 个元素到 result 右侧
        consumeComponentToEnvRight result, tokenScanner, offset
        ' 5. 执行: 吸收一个同行注释到 result 右侧
        consumeOneShareLineCommentToEnvRight result, tokenScanner
        ' 6. 进行下一轮遍历
    Wend
whileBreak:

    ' ==================================================
    ' 5. 处理表定义
    ' ==================================================
    ' 1. 检查: 如果扫描器没用完，则继续
    If tokenScanner.hasNext Then
        ' 2. 执行: [解析连续出现的多个表定义](#解析连续出现的多个表定义)，结果保存到 `elem`
        Set elem = parseTableReferences(tokenScanner)
        ' 3. 将 `elem` 保存到 `result`
        result.ISqlFormatterElem_addComponent elem
    End If

    ' ==================================================
    ' 6. 退出处理，并返回 `result`
    ' ==================================================
    ' return result
    Set parseUpdateTableReferencesEnv = result
End Function

' 解析update的set作用域
'
' @param tokenScanner 扫描器
' @return update的set作用域
Public Function parseUpdateSetEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 处理 set 关键字
    ' ==================================================
    ' 1. 从扫描器中弹出一个 token 保存到 `ISqlFormatterElem result`: `getNext`
    '    Dim result As ISqlFormatterElem
    '    这里直接使用 parseUpdateSetEnv
    Set parseUpdateSetEnv = tokenScanner.getNext
    ' 2. 为 result 吸收一个同行注释并合并为TokenGroup，结果保存到 `result`
    Set parseUpdateSetEnv = consumeShareLineCommentToTokenGroup(parseUpdateSetEnv, tokenScanner)
    ' 3. 检查: 如果扫描器已经用完，则结束并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If

    ' ==================================================
    ' 2. 解析赋值语句列表作用域
    ' ==================================================
    ' 1. 执行: 解析赋值语句列表作用域，结果保存到 `ISqlFormatterElem elem`
    Dim elem As ISqlFormatterElem
    Set elem = parseAssignmentListEnv(tokenScanner)
    ' 2. 检查: 如果 `elem` 是空，则退出处理，并返回 `result`
    If elem Is Nothing Then
        ' return result
        Exit Function
    End If

    ' 3. 将 `result` 包装为一个类型为 `SqlTokenType.KEY_SET` 的 `SqlFormatterKeySectionEnv` 作用域，结果保存到 `result`
    Set parseUpdateSetEnv = SqlFormatterHelper.createKeyStartEnv(parseUpdateSetEnv, SqlTokenType.KEY_SET)

    ' 4. 将 `elem` 保存到 `result`
    parseUpdateSetEnv.addComponent elem

    ' ==================================================
    ' 3. 退出处理，并返回 `result`
    ' ==================================================
    ' return result
End Function

' 解析 delete sql片段，并生成作用域
'
' @param tokenScanner 扫描器
' @return delete 作用域
Public Function parseDelete(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' 1. 获取 delete sql 组成部分的解析器: `getDeleteAnalyzer`
    ' 2. 执行: 各类型sql的组成部分的通用解析流程，并返回执行结果
    Set parseDelete = parseSqlComposition(getDeleteAnalyzer, tokenScanner)
End Function

' 解析delete作用域的起始部分
'
' @param tokenScanner 扫描器
' @return delete作用域的起始部分
Public Function parseDeleteStartEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 筛选项: `ISqlFormatterElem elem = null`
    Dim elem As ISqlFormatterElem

    ' ==================================================
    ' 2. 初始化目标作用域
    ' ==================================================
    ' 1. 从扫描器中弹出一个 token 保存到 `elem`
    Set elem = tokenScanner.getNext
    ' 2. 为 elem 吸收一个同行注释并合并为TokenGroup，结果保存到 `elem`
    Set elem = consumeShareLineCommentToTokenGroup(elem, tokenScanner)
    ' 3. 检查: 如果扫描器已经用完，则结束并返回 `elem`
    If Not tokenScanner.hasNext Then
        ' return elem
        Set parseDeleteStartEnv = elem
        Exit Function
    End If

    ' 4. 创建作用域 `SqlFormatterKeySectionEnv result`，并使用 `elem` 和 `SqlTokenType.KEY_DELETE` 初始化
    Dim result As SqlFormatterKeySectionEnv
    Set result = New SqlFormatterKeySectionEnv
    result.Init elem, SqlTokenType.KEY_DELETE

    ' ==================================================
    ' 3. 初始化变量
    ' ==================================================
    ' 1. 偏移量 `int offset`
    Dim offset As Integer

    ' ==================================================
    ' 4. 吸收: 注释或者 delete 的行为关键字
    ' ==================================================
    ' 1. 开始遍历，直到扫描器用完
    While tokenScanner.hasNext
        ' 2. 执行: 获取扫描器到下一个最近的delete行为关键字的偏移量，结果保存到 `offset`
        offset = SqlFormatterHelper.getNextDeleteStartActionSqlKeyOffset(tokenScanner)
        ' 3. 检查: 如果 `offset == 0`，则退出遍历
        If offset = 0 Then
            GoTo whileBreak
        End If

        ' 4. 执行: 吸收 offset 个元素到 result 右侧
        consumeComponentToEnvRight result, tokenScanner, offset
        ' 5. 执行: 吸收一个同行注释到 result 右侧
        consumeOneShareLineCommentToEnvRight result, tokenScanner
        ' 6. 进行下一轮遍历
    Wend
whileBreak:

    ' ==================================================
    ' 5. 处理表定义
    ' ==================================================
    ' 1. 检查: 如果扫描器没用完，则继续
    If tokenScanner.hasNext Then
        ' 2. 执行: 解析连续出现的多个表定义，结果保存到 `elem`
        Set elem = parseTableReferences(tokenScanner)
        ' 3. 将 `elem` 保存到 `result`
        result.ISqlFormatterElem_addComponent elem
    End If

    ' ==================================================
    ' 6. 退出处理，并返回 `result`
    ' ==================================================
    ' return result
    Set parseDeleteStartEnv = result
End Function

' 解析 inset sql片段，并生成作用域
'
' @param tokenScanner 扫描器
' @return delete 作用域
Public Function parseInsert(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' 1. 获取 insert sql 组成部分的解析器: `getInsertAnalyzer`
    ' 2. 执行: 各类型sql的组成部分的通用解析流程，并返回执行结果
    Set parseInsert = parseSqlComposition(getInsertAnalyzer, tokenScanner)
End Function

' 解析insert作用域的起始部分
'
' @param tokenScanner 扫描器
' @return insert作用域的起始部分
Public Function parseInsertStartEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 筛选项: `ISqlFormatterElem elem = null`
    Dim elem As ISqlFormatterElem

    ' ==================================================
    ' 2. 初始化目标作用域
    ' ==================================================
    ' 1. 从扫描器中弹出一个 token 保存到 `elem`
    Set elem = tokenScanner.getNext
    ' 2. 为 elem 吸收一个同行注释并合并为TokenGroup，结果保存到 `elem`
    Set elem = consumeShareLineCommentToTokenGroup(elem, tokenScanner)
    ' 3. 检查: 如果扫描器已经用完，则结束并返回 `elem`
    If Not tokenScanner.hasNext Then
        ' return elem
        Set parseInsertStartEnv = elem
        Exit Function
    End If

    ' 4. 创建作用域 `SqlFormatterKeySectionEnv result`，并使用 `elem` 和 `SqlTokenType.KEY_INSERT` 初始化
    Dim result As SqlFormatterKeySectionEnv
    Set result = New SqlFormatterKeySectionEnv
    result.Init elem, SqlTokenType.KEY_INSERT

    ' ==================================================
    ' 3. 初始化变量
    ' ==================================================
    ' 1. 偏移量 `int offset`
    Dim offset As Integer

    ' ==================================================
    ' 4. 吸收: 注释或者 insert 的行为关键字
    ' ==================================================
    ' 1. 开始遍历，直到扫描器用完
    While tokenScanner.hasNext
        ' 2. 执行: 获取扫描器到下一个最近的insert行为关键字的偏移量，结果保存到 `offset`
        offset = SqlFormatterHelper.getNextInsertStartActionSqlKeyOffset(tokenScanner)
        ' 3. 检查: 如果 `offset == 0`，则退出遍历
        If offset = 0 Then
            GoTo whileBreak
        End If

        ' 4. 执行: 吸收 offset 个元素到 result 右侧
        consumeComponentToEnvRight result, tokenScanner, offset
        ' 5. 执行: 吸收一个同行注释到 result 右侧
        consumeOneShareLineCommentToEnvRight result, tokenScanner
        ' 6. 进行下一轮遍历
    Wend
whileBreak:

    ' ==================================================
    ' 5. 退出处理，并返回 `result`
    ' ==================================================
    ' return result
    Set parseInsertStartEnv = result
End Function

' 解析into作用域
'
' @param tokenScanner
' @return into 作用域
Public Function parseIntoEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 临时元素: `ISqlFormatterElem elem = null`
    Dim elem As ISqlFormatterElem

    ' ==================================================
    ' 2. 初始化目标作用域
    ' ==================================================
    ' 1. 从扫描器中弹出一个 token 保存到 `elem`
    Set elem = tokenScanner.getNext
    ' 2. 为 elem 吸收一个同行注释并合并为TokenGroup，结果保存到 `elem`
    Set elem = consumeShareLineCommentToTokenGroup(elem, tokenScanner)
    ' 3. 检查: 如果扫描器已经用完，则结束并返回 `elem`
    If Not tokenScanner.hasNext Then
        ' return elem
        Set parseIntoEnv = elem
        Exit Function
    End If
    ' 4. 创建作用域 `ISqlFormatterElem result`，并使用 `elem` 和 `SqlTokenType.KEY_INTO` 初始化
    '    这里直接使用 parseIntoEnv
    Set parseIntoEnv = SqlFormatterHelper.createKeyStartEnv(elem, SqlTokenType.KEY_INTO)

    ' ==================================================
    ' 3. 处理表定义
    ' ==================================================
    ' 1. 检查: 如果扫描器没用完，则继续
    If tokenScanner.hasNext Then
        ' 2. 从扫码中获取下一个 token 保存到 `elem`
        Set elem = tokenScanner.peek
        ' 3. 检查: 如果 `elem` 的类型是 `SqlTokenType.STR_FUNC`
        If elem.getType = SqlTokenType.STR_FUNC Then
            ' 3.2. 向前推进扫描器
            tokenScanner.toNext

        ' 4. 否则
        Else
            ' 4.1. 执行: 匹配一个表定义，结果保存到 `elem`
            Set elem = parseTableFactor(tokenScanner)
            ' 4.2. 检查: 如果 `elem` 是空，则退出处理，并返回 `result`
            If elem Is Nothing Then
                ' return result
                Exit Function
            End If
        End If
        ' 5. 将 `elem` 保存到 `result`
        parseIntoEnv.addComponent elem
    End If

    ' ==================================================
    ' 4. 处理列名
    ' ==================================================
    ' 1. 声明变量: 偏移量 `int offset`
    Dim offset As Integer
    ' 2. 执行: 获取扫描器到下一个最近的左括号的偏移量，结果保存到 `offset`
    offset = SqlFormatterHelper.getNextLeftParenthesisOffset(tokenScanner)
    ' 3. 检查: 如果 `offset > 0`，则继续
    If offset > 0 Then
        ' 4. 检查: 如果 `offset > 1`，则执行:吸收 offset - 1 个注释元素到 result 右侧
        If offset > 1 Then
            consumeComponentToEnvRight parseIntoEnv, tokenScanner, offset - 1
        End If

        ' 5. 执行: 匹配括号包裹的多个列对象，结果保存到 `elem`
        '    逗号后面是否需要换行，需要按照配置对象的属性来设置
        Set elem = parseParenthesisWrapperMultiCol( _
            tokenScanner, config.insertColListNeedLineBreakAfterComma)

        ' 6. 将 `elem` 添加到 `result`
        parseIntoEnv.addComponent elem
    End If

    ' ==================================================
    ' 5. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 解析insert的rowAlias
'
' @param tokenScanner 扫描器
' @return insert的rowAlias作用域对象
Public Function parseRowAliasEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 临时元素: `ISqlFormatterElem elem = null`
    Dim elem As ISqlFormatterElem

    ' ==================================================
    ' 2. 获取 key
    ' ==================================================
    ' 1. 从扫描器中弹出一个 token 保存到 `elem`
    Set elem = tokenScanner.getNext
    ' 2. 为 elem 吸收一个同行注释并合并为TokenGroup，结果保存到 `elem`
    Set elem = consumeShareLineCommentToTokenGroup(elem, tokenScanner)

    ' 3. 检查: 如果扫描器已经用完，则结束并返回 `elem`
    If Not tokenScanner.hasNext Then
        ' return elem
        Set parseRowAliasEnv = elem
        Exit Function
    End If

    ' ==================================================
    ' 3. 声明变量
    ' ==================================================
    ' 1. 偏移量 `int offset`
    Dim offset As Integer

    ' ==================================================
    ' 4. 处理 row_alias
    ' ==================================================
    ' 1. 执行: 获取扫描器到下一个rowAlias的偏移量，结果保存到 `offset`
    offset = SqlFormatterHelper.getNextRowAliasOffset(tokenScanner)
    ' 2. 检查: 如果 `offset == 0`，则退出处理，并返回 `elem`
    If offset = 0 Then
        ' return elem
        Set parseRowAliasEnv = elem
        Exit Function
    End If

    ' 3. 创建作用域 `ISqlFormatterElem result`，并使用 `elem` 和 `SqlTokenType.KEY_AS` 初始化
    '    这里直接使用
    Set parseRowAliasEnv = SqlFormatterHelper.createKeyStartEnv(elem, SqlTokenType.KEY_AS)

    ' 4. 检查: 如果 `offset > 1`，则执行:吸收 offset - 1 个注释元素到 result 右侧
    If offset > 1 Then
        consumeComponentToEnvRight parseRowAliasEnv, tokenScanner, offset - 1
    End If

    ' 5. 从扫描器中弹出一个 token，并保存到 `elem`
    Set elem = tokenScanner.getNext
    ' 6. 为 elem 吸收一个同行注释并合并为TokenGroup，结果保存到 `elem`
    Set elem = consumeShareLineCommentToTokenGroup(elem, tokenScanner)

    ' ==================================================
    ' 5. 处理列名
    ' ==================================================
    ' 1. 执行: [获取扫描器到下一个最近的左括号的偏移量](./SqlFormatterHelper.design.md#获取扫描器到下一个最近的左括号的偏移量)，结果保存到 `offset`
    offset = SqlFormatterHelper.getNextLeftParenthesisOffset(tokenScanner)
    ' 2. 检查: 如果 `offset > 0`，则继续
    If offset > 0 Then
        ' 3. 创建 rowAlias 容器 `elem = SqlFormatterHelper.wrapperColToContainerCol(elem)`
        Set elem = SqlFormatterHelper.wrapperColToContainerCol(elem)
        ' 4. 检查: 如果 `offset > 1`，则执行:[吸收 offset - 1 个注释元素到 elem 右侧](#吸收多个元素到作用域右侧)
        If offset > 1 Then
            consumeComponentToEnvRight elem, tokenScanner, offset - 1
        End If

        ' 5. 执行: 匹配括号包裹的多个列对象，结果保存到 `ISqlFormatterElem cols`
        Dim cols As ISqlFormatterElem
        Set cols = parseParenthesisWrapperMultiCol(tokenScanner)
        ' 6. 将 `cols` 添加到 `elem`
        elem.addComponent cols
    End If
    
    ' ==================================================
    ' 6. 保存 rowAlias 部分
    ' ==================================================
    ' 1. 将 `elem` 保存到 `result`
    parseRowAliasEnv.addComponent elem

    ' ==================================================
    ' 7. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 解析insert的values
'
' @param tokenScanner
' @return insert的value 作用域
Public Function parseInsetValuesEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 临时元素: `ISqlFormatterElem elem = null`
    Dim elem As ISqlFormatterElem
    ' 2. keyType: `SqlTokenType keyType`
    Dim keyType As SqlTokenType

    ' ==================================================
    ' 2. 获取 key
    ' ==================================================
    ' 1. 从扫描器中弹出一个 token 保存到 `elem`: `getNext`
    Set elem = tokenScanner.getNext
    ' 2. 保存 `elem` 的类型到 `keyType`
    keyType = elem.getType
    ' 3. 为 elem 吸收一个同行注释并合并为TokenGroup，结果保存到 `elem`
    Set elem = consumeShareLineCommentToTokenGroup(elem, tokenScanner)
    ' 4. 检查: 如果扫描器已经用完，则结束并返回 `elem`
    If Not tokenScanner.hasNext Then
        ' return elem
        Set parseInsetValuesEnv = elem
        Exit Function
    End If

    ' ==================================================
    ' 3. 声明变量
    ' ==================================================
    ' 1. 声明变量: 偏移量 `int offset`
    Dim offset As Integer
    ' 2. 保存所有 value 的容器 `ISqlFormatterElem values`
    Dim values As ISqlFormatterElem

    ' ==================================================
    ' 4. 处理一组 value
    ' ==================================================
    ' 1. 执行: 获取扫描器到下一个最近的左括号的偏移量，结果保存到 `offset`
    offset = SqlFormatterHelper.getNextLeftParenthesisOffset(tokenScanner)
    ' 2. 检查: 如果 `offset == 0`，则退出处理，并返回 `elem`
    If offset = 0 Then
        ' return elem
        Set parseInsetValuesEnv = elem
        Exit Function
    End If
    ' 3. 创建作用域 `ISqlFormatterElem result`，并使用 `elem` 和 `keyType` 初始化: `SqlFormatterHelper.createKeyStartEnv`
    '    这里直接使用 parseInsetValuesEnv
    Set parseInsetValuesEnv = SqlFormatterHelper.createKeyStartEnv(elem, keyType)

    ' 4. 检查: 如果 `offset > 1`
    If offset > 1 Then
        ' 4.1. 创建 `values`
        Set values = SqlFormatterHelper.createCommaDelimitMultiElem(True)
        ' 4.2. 将 `values` 保存到 `result`
        parseInsetValuesEnv.addComponent values
        ' 4.3. 执行:吸收 offset - 1 个注释元素到 values 右侧
        consumeComponentToEnvRight values, tokenScanner, offset - 1
    End If
    ' 5. 执行: 匹配括号包裹的多个列对象，结果保存到 `elem`
    '    逗号后面是否需要换行，需要按照配置对象的属性来设置
    Set elem = parseParenthesisWrapperMultiCol( _
        tokenScanner, config.insertColListNeedLineBreakAfterComma)
    
    ' ==================================================
    ' 5. 检查是不是有多组 value
    ' ==================================================
    ' 1. 执行: 获取扫描器到下一个最近的逗号的偏移量，结果保存到 `offset`
    offset = SqlFormatterHelper.getNextCommaOffset(tokenScanner)
    ' 2. 检查: 如果 `offset == 0`
    If offset = 0 Then
        ' 2.1. 检查: 如果 `values` 是空，则将 `elem` 保存到 `result`
        If values Is Nothing Then
            parseInsetValuesEnv.addComponent elem

        ' 2.2. 否则，将 `elem` 保存到 `values`
        Else
            values.addComponent elem
        End If
        ' 2.3. 退出处理，并返回 `result`
        ' return result
        Exit Function
    End If
    ' 3. 检查: 如果 `values` 是空，则创建 `values`
    If values Is Nothing Then
        ' 3.1. 创建 `values`
        Set values = SqlFormatterHelper.createCommaDelimitMultiElem(True)
        ' 3.2. 将 `values` 保存到 `result`
        parseInsetValuesEnv.addComponent values
    End If
    ' 4. 将 `elem` 保存到 `values`
    values.addComponent elem

    ' ==================================================
    ' 6. 收集剩余的 value
    ' ==================================================
    ' 6.1. 遍历吸收剩余的 value, 遍历条件 `offset > 0`
    While offset > 0
        ' ==============================
        ' 6.2. 吸收逗号
        ' ==============================
        ' 1. 执行: 吸收 offset 个元素到 values 右侧
        consumeComponentToEnvRight values, tokenScanner, offset

        ' ==============================
        ' 6.3. 吸收一组 value
        ' ==============================
        ' 1. 执行: 获取扫描器到下一个最近的左括号的偏移量，结果保存到 `offset`
        offset = SqlFormatterHelper.getNextLeftParenthesisOffset(tokenScanner)
        ' 2. 检查: 如果 `offset == 0`，则退出遍历
        If offset = 0 Then
            GoTo whileBreak

        ' 3. 检查: 如果 `offset > 1`
        ElseIf offset > 1 Then
            ' 3.1. 执行: 吸收 offset - 1 个注释元素到 values 右侧
            consumeComponentToEnvRight values, tokenScanner, offset - 1
        End If
        ' 4. 执行: 匹配括号包裹的多个列对象，结果保存到 `elem`
        '    逗号后面是否需要换行，需要按照配置对象的属性来设置
        Set elem = parseParenthesisWrapperMultiCol( _
            tokenScanner, config.insertColListNeedLineBreakAfterComma)
        ' 5. 检查: 如果 `elem == null`，退出遍历
        If elem Is Nothing Then
            GoTo whileBreak
        End If
        ' 6. 将 `elem` 保存到 `values`
        values.addComponent elem

        ' ==============================
        ' 6.4. 检查下一个逗号
        ' ==============================
        ' 1. 执行: 获取扫描器到下一个最近的逗号的偏移量，结果保存到 `offset`: `SqlFormatterHelper.getNextCommaOffset`
        offset = SqlFormatterHelper.getNextCommaOffset(tokenScanner)

        ' ==============================
        ' 6.5. 进入下一轮遍历
        ' ==============================
    Wend
whileBreak:

    ' ==================================================
    ' 7. 吸收注释
    ' ==================================================
    ' 1. 对 `values` 执行: 吸收一个同行注释到作用域右侧
    consumeOneShareLineCommentToEnvRight values, tokenScanner

    ' ==================================================
    ' 8. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 解析ON_DUPLICATE作用域
'
' @param tokenScanner 扫描器
' @return ON_DUPLICATE作用域
Public Function parseInsertOnDuplicateEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 临时元素 `ISqlFormatterElem elem`
    Dim elem As ISqlFormatterElem
    ' 2. token `SqlToken token`
    Dim token As SqlToken

    ' ==================================================
    ' 2. 处理第一个 key
    ' ==================================================
    ' 1. 标记扫描器: `mark`
    tokenScanner.mark
    ' 2. 从扫描器中弹出一个 token，保存到 `elem`
    Set elem = tokenScanner.getNext
    ' 3. 检查: 如果扫描器已经用完
    If Not tokenScanner.hasNext Then
        ' 3.1. 清除标记: `clearMark`
        tokenScanner.clearMark
        ' 3.2. 退出处理，并返回 `elem`
        ' return elem
        Set parseInsertOnDuplicateEnv = elem
        Exit Function
    End If

    ' ==================================================
    ' 3. 处理第二个 key
    ' ==================================================
    ' 1. 从扫描器中获取一个 token，保存到 `token`
    Set token = tokenScanner.peek
    ' 2. 检查: 如果 `token` 的类型是不是 `DUPLICATE`
    If token.tokenType <> SqlTokenType.KEY_DUPLICATE Then
        ' 2.1. 重置扫描器位置并清除标记: `resetAndClearMark`
        tokenScanner.resetAndClearMark
        ' 2.2. 退出处理，并返回 null
        ' return null
        Exit Function
    End If

    ' 3. 向前推进扫描器
    tokenScanner.toNext

    ' 4. 创建 `SqlFormatterTokenGroup keyGroup`
    Dim keyGroup As SqlFormatterTokenGroup
    Set keyGroup = New SqlFormatterTokenGroup

    ' 5. 将 `elem` + `token` 保存到 `keyGroup`
    keyGroup.ISqlFormatterElem_addComponent elem
    keyGroup.ISqlFormatterElem_addComponent token
    ' 6. 清除标记: `clearMark`
    tokenScanner.clearMark
    ' 7. 检查: 如果扫描器已经用完，则退出处理，并返回 `keyGroup`
    If Not tokenScanner.hasNext Then
        ' return keyGroup
        Set parseInsertOnDuplicateEnv = keyGroup
        Exit Function
    End If

    ' ==================================================
    ' 4. 处理第三个 key
    ' ==================================================
    ' 1. 从扫描器中获取一个 token，保存到 `token`
    Set token = tokenScanner.peek
    ' 2. 检查: 如果 `token` 的类型是不是 `KEY`，则退出处理，并返回 `keyGroup`
    If token.tokenType <> SqlTokenType.KEY_KEY Then
        ' return keyGroup
        Set parseInsertOnDuplicateEnv = keyGroup
        Exit Function
    End If
    ' 3. 向前推进扫描器
    tokenScanner.toNext
    ' 4. 将 `token` 保存到 `keyGroup`
    keyGroup.ISqlFormatterElem_addComponent token
    ' 5. 检查: 如果扫描器已经用完，则退出处理，并返回 `keyGroup`
    If Not tokenScanner.hasNext Then
        ' return keyGroup
        Set parseInsertOnDuplicateEnv = keyGroup
        Exit Function
    End If

    ' ==================================================
    ' 5. 处理第四个 key
    ' ==================================================
    ' 1. 从扫描器中获取一个 token，保存到 `token`
    Set token = tokenScanner.peek
    ' 2. 检查: 如果 `token` 的类型是不是 `UPDATE`，则退出处理，并返回 `keyGroup`
    If token.tokenType <> SqlTokenType.KEY_UPDATE Then
        ' return keyGroup
        Set parseInsertOnDuplicateEnv = keyGroup
        Exit Function
    End If
    ' 3. 向前推进扫描器
    tokenScanner.toNext
    ' 4. 将 `token` 保存到 `keyGroup`
    keyGroup.ISqlFormatterElem_addComponent token
    ' 5. 检查: 如果扫描器已经用完，则退出处理，并返回 `keyGroup`
    If Not tokenScanner.hasNext Then
        ' return keyGroup
        Set parseInsertOnDuplicateEnv = keyGroup
        Exit Function
    End If

    ' ==================================================
    ' 6. 解析赋值语句列表作用域
    ' ==================================================
    ' 1. 执行: 解析赋值语句列表作用域，结果保存到 `ISqlFormatterElem elem`
    Set elem = parseAssignmentListEnv(tokenScanner)
    ' 2. 检查: 如果 `elem` 是空，则退出处理，并返回 `keyGroup`
    If elem Is Nothing Then
        ' return keyGroup
        Set parseInsertOnDuplicateEnv = keyGroup
        Exit Function
    End If
    ' 3. 将 `keyGroup` 包装为一个类型为 `SqlTokenType.KEY_DUPLICATE` 的 `SqlFormatterKeySectionEnv` 作用域，结果保存到 `ISqlFormatterElem result`
    '    这里直接使用 parseInsertOnDuplicateEnv
    Set parseInsertOnDuplicateEnv = SqlFormatterHelper.createKeyStartEnv(keyGroup, SqlTokenType.KEY_DUPLICATE)

    ' 4. 将 `elem` 保存到 `result`
    parseInsertOnDuplicateEnv.addComponent elem

    ' ==================================================
    ' 7. 退出处理，并返回 `result`
    ' ==================================================
    ' return result
End Function

' 解析赋值语句列表作用域
'
' @param tokenScanner 扫描器
' @return 赋值语句列表作用域
Public Function parseAssignmentListEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 偏移量 `int offset`
    Dim offset As Integer
    ' 2. 临时元素: `ISqlFormatterElem elem`
    Dim elem As ISqlFormatterElem
    ' 3. 赋值语句列表作用域: `SqlFormatterAssignmentListEnv assignmentList`
    Dim result As SqlFormatterAssignmentListEnv

    ' ==================================================
    ' 2. 吸收列开始前的注释
    ' ==================================================
    ' 1. 执行: 获取扫描器到下一个最近的不是sql各部分的起始关键字的偏移量，结果保存到 `offset`
    offset = SqlFormatterHelper.getNextNonSectionStartSqlKeyOffset(tokenScanner)
    ' 2. 检查: 如果不存在筛选项 `offset == 0`，则退出处理，并返回 `null`
    If offset = 0 Then
        ' return null
        Exit Function
    End If

    ' 3. 创建赋值语句列表作用域 `result = new SqlFormatterAssignmentListEnv()`
    Set result = New SqlFormatterAssignmentListEnv

    ' 4. 检查: 如果 `offset > 1`，执行:吸收 offset - 1 个注释元素到 result 右侧
    If offset > 1 Then
        consumeComponentToEnvRight result, tokenScanner, offset - 1
    End If

    ' ==================================================
    ' 3. 解析第一个赋值语句
    ' ==================================================
    ' 1. 执行: 匹配一个赋值语句，结果保存到 `elem`
    Set elem = parseAssignment(tokenScanner, Nothing)

    ' 2. 检查: 如果 `elem` 是空，则退出处理，并返回 `result`
    If elem Is Nothing Then
        ' return result
        Set parseAssignmentListEnv = result
        Exit Function
    End If

    ' 3. 将 `elem` 保存到 `result`
    result.ISqlFormatterElem_addComponent elem

    ' ==================================================
    ' 4. 收集剩余的赋值语句
    ' ==================================================
    ' 4.1. 遍历扫描器，直到扫描器用完
    While tokenScanner.hasNext
        ' ==============================
        ' 4.2. 吸收逗号
        ' ==============================
        ' 1. 执行: 获取扫描器到下一个最近的逗号的偏移量，结果保存到 `offset`
        offset = SqlFormatterHelper.getNextCommaOffset(tokenScanner)

        ' 2. 检查: 如果 `offset == 0`，则退出遍历
        If offset = 0 Then
            GoTo whileBreak
        End If
        ' 3. 执行: 吸收 offset 个元素到 result 右侧
        consumeComponentToEnvRight result, tokenScanner, offset

        ' ==============================
        ' 4.3. 吸收赋值语句
        ' ==============================
        ' 1. 执行: 获取扫描器到下一个最近的不是sql各部分的起始关键字的偏移量，结果保存到 `offset`
        offset = SqlFormatterHelper.getNextNonSectionStartSqlKeyOffset(tokenScanner)
        ' 2. 检查: 如果 `offset == 0`，则退出遍历
        If offset = 0 Then
            GoTo whileBreak

        ' 3. 检查: 如果 `offset > 1`
        ElseIf offset > 1 Then
            ' 3.1. 执行:吸收 offset - 1 个注释元素到 result 右侧
            consumeComponentToEnvRight result, tokenScanner, offset - 1
        End If

        ' 4. 执行: 匹配一个赋值语句，结果保存到 `elem`
        Set elem = parseAssignment(tokenScanner, Nothing)
        ' 5. 检查: 如果 `elem == null`，退出遍历
        If elem Is Nothing Then
            GoTo whileBreak
        End If
        ' 6. 将 `elem` 保存到 `result`
        result.ISqlFormatterElem_addComponent elem

        ' ==============================
        ' 4.4. 进入下一轮遍历
        ' ==============================
    Wend
whileBreak:

    ' ==================================================
    ' 5. 吸收注释
    ' ==================================================
    ' 1. 对 `result` 执行: 吸收一个同行注释到作用域右侧，结果保存到 `result`
    Set result = consumeOneShareLineCommentToEnvRight(result, tokenScanner)

    ' ==================================================
    ' 6. 退出处理，并返回 `result`
    ' ==================================================
    ' return result
    Set parseAssignmentListEnv = result
End Function

' 解析from作用域
'
' @param tokenScanner 扫描器
' @return from作用域对象
Public Function parseFromEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    Set parseFromEnv = parseKeyStartTableEnv(tokenScanner)
End Function

' 解析using作用域
'
' @param tokenScanner 扫描器
' @return using作用域对象
Public Function parseUsingEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    Set parseUsingEnv = parseKeyStartTableEnv(tokenScanner)
End Function

' 解析from或using作用域的通用处理
'
' @param tokenScanner 扫描器
' @return from作用域对象
Public Function parseKeyStartTableEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 parseKeyStartTableEnv
    ' 2. 表定义集合 `ISqlFormatterElem tables`
    Dim tables As ISqlFormatterElem
    ' 3. 作用域的类型 `SqlTokenType envType`
    Dim envType As SqlTokenType

    ' ==================================================
    ' 2. 处理第一个 token
    ' ==================================================
    ' 1. 从扫描器中弹出一个 token，保存到 `result`
    Set parseKeyStartTableEnv = tokenScanner.getNext
    ' 2. 保存 result 的类型到 `envType`
    envType = parseKeyStartTableEnv.getType

    ' 3. 为 result 吸收一个同行注释并合并为TokenGroup，结果保存到 `result`
    Set parseKeyStartTableEnv = consumeShareLineCommentToTokenGroup(parseKeyStartTableEnv, tokenScanner)

    ' 4. 检查: 如果扫描器已经用完，则退出处理，返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If
    ' 5. 初始化结果对象 `result`
    Set parseKeyStartTableEnv = SqlFormatterHelper.createKeyStartEnv(parseKeyStartTableEnv, envType)

    ' ==================================================
    ' 3. 处理表定义
    ' ==================================================
    ' 1. 检查: 如果扫描器没用完，则继续
    If tokenScanner.hasNext Then
        ' 2. 执行: 解析连续出现的多个表定义，结果保存到 `tables`
        '    因为前面存在 from，所以这里不使用参数中的左括号，需要在方法内发现、解析开头的左括号
        Set tables = parseTableReferences(tokenScanner)
        ' 3. 将 `tables` 保存到 `result`
        parseKeyStartTableEnv.addComponent tables
    End If

    ' ==================================================
    ' 4. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 解析 join
'
' @param tokenScanner
' @return join 作用域
Public Function parseJoinEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 parseJoinEnv
    ' 2. 表定义集合 `ISqlFormatterElem tableFactor`
    Dim tableFactor As ISqlFormatterElem

    ' ==================================================
    ' 2. 匹配 join 关键字
    ' ==================================================
    ' 1. 执行: 解析join关键字，结果保存到 `result`
    Set parseJoinEnv = parseJoinKey(tokenScanner)

    ' 2. 为 result 吸收一个同行注释并合并为TokenGroup，结果保存到 `result`
    Set parseJoinEnv = consumeShareLineCommentToTokenGroup(parseJoinEnv, tokenScanner)

    ' 3. 检查: 如果扫描器已经用完，则退出处理，返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If

    ' 4. 初始化结果对象 `result`
    Set parseJoinEnv = SqlFormatterHelper.createKeyStartEnv(parseJoinEnv, SqlTokenType.KEY_JOIN)

    ' ==================================================
    ' 3. 处理表定义
    ' ==================================================
    ' 1. 检查: 如果扫描器没用完，则继续
    If tokenScanner.hasNext Then
        ' 2. 执行: 匹配一个表定义，结果保存到 `tableFactor`
        '    因为前面存在 join 关键字，所以这里不使用参数中的左括号，需要在方法内发现、解析开头的左括号
        Set tableFactor = parseTableFactor(tokenScanner)
        ' 3. 将 `tableFactor` 保存到 `result`
        parseJoinEnv.addComponent tableFactor
    End If

    ' ==================================================
    ' 4. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 解析join关键字
' 需要匹配的内容
'    [left[outer]|right[outer]|inner|cross] join
'
' @param tokenScanner 扫描器
' @return join 关键字或者关键字组合
Public Function parseJoinKey(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. token `SqlToken token`
    Dim token As SqlToken
    ' 2. tokenType `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType
    ' 3. 结果对象 `SqlFormatterTokenGroup result`
    Dim result As SqlFormatterTokenGroup

    ' ==================================================
    ' 2. 处理第一个元素
    ' ==================================================
    ' 1. 从扫描器中弹出一个 `token`，并保存 `tokenType`
    Set token = tokenScanner.getNext
    tokenType = token.tokenType

    ' 2. 检查: 如果 token 的类型是 join，则退出处理，并返回 `token`
    If tokenType = SqlTokenType.KEY_JOIN Then
        Set parseJoinKey = token
        Exit Function
    End If

    ' 3. 检查: 如果扫描器已经用完了，则退出处理，并返回 `token`
    If Not tokenScanner.hasNext Then
        Set parseJoinKey = token
        Exit Function
    End If

    ' 4. 创建结果对象 `result = new SqlFormatterTokenGroup()`
    Set result = New SqlFormatterTokenGroup

    ' 5. 设置类型
    result.setType SqlTokenType.KEY_JOIN

    ' 6. 将 `token` 保存到 `result`
    result.ISqlFormatterElem_addComponent token

    ' ==================================================
    ' 3. 处理 left / right 之后的 outer
    ' ==================================================
    ' 1. 检查: 如果 `tokenType` 是 left 或者 right，则继续
    If tokenType = SqlTokenType.KEY_LEFT Or tokenType = SqlTokenType.KEY_RIGHT Then
        ' 2. 从扫描器中获取一个 `token`
        Set token = tokenScanner.peek
        ' 3. 检查: `token` 类型是 outer
        If token.tokenType = SqlTokenType.KEY_OUTER Then
            ' 4. 向前推进扫描器
            tokenScanner.toNext

            ' 5. 将 `token` 保存到 `result`
            result.ISqlFormatterElem_addComponent token

            ' 6. 检查: 如果扫描器已经用完了，则退出处理，并返回 `result`
            If Not tokenScanner.hasNext Then
                Set parseJoinKey = result
                Exit Function
            End If
        End If
    End If

    ' ==================================================
    ' 4. 处理最后一个 join
    ' ==================================================
    ' 1. 从扫描器中获取一个 `token`
    Set token = tokenScanner.peek
    ' 2. 检查: `token` 类型是 join，则继续
    If token.tokenType = SqlTokenType.KEY_JOIN Then
        ' 3. 向前推进扫描器
        tokenScanner.toNext
        ' 4. 将 `token` 保存到 `result`
        result.ISqlFormatterElem_addComponent token
    End If

    ' ==================================================
    ' 5. 退出处理，并返回 `result`
    ' ==================================================
    Set parseJoinKey = result
End Function

' 匹配真实列或全数据列
' 需要匹配的内容
'       colName -> colNameVal
'                | tableName.colNameVal
'    colNameVal -> str_name | *
' @return ISqlFormatterElem
Public Function parseColFromStrName(ByRef tokenScanner As ObjArrayRefScanner, ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 第一个名称 token `SqlToken firstNameToken`
    Dim firstNameToken As SqlToken
    ' 2. token `SqlToken token`
    Dim token As SqlToken
    ' 3. 复合结果对象 `ISqlFormatterElem result`
    Dim result As ISqlFormatterElem

    ' ==================================================
    ' 2. 处理第一个 token
    ' ==================================================
    ' 1. 从扫描器中弹出一个 token，保存到 `firstNameToken`
    Set firstNameToken = tokenScanner.getNext
    ' 2. 检查: 如果扫描器已经用完，则退出处理，并返回 `firstNameToken`
    If Not tokenScanner.hasNext Then
        Set parseColFromStrName = firstNameToken
        Exit Function
    End If
    ' 3. 对 `firstNameToken` 执行: 列对象括号配对，结果保存到 `result`
    Set result = matchColBoundaryParenthesis(firstNameToken, tokenScanner, leftParenthesisStack)
    ' 4. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Set parseColFromStrName = result
        Exit Function
    End If

    ' ==================================================
    ' 3. 尝试处理 tableName.colNameVal 中的 `.`
    ' ==================================================
    ' 1. 从扫描器中获取一个 token (第二个 token) 到 `token`
    Set token = tokenScanner.peek
    ' 2. 检查: 如果 token 类型不是 `SqlTokenType.POINT`
    If token.tokenType <> SqlTokenType.POINT Then
        ' 2.1. 退出处理，返回 `result`
        Set parseColFromStrName = result
        Exit Function
    End If

    ' 3. 向前推进扫描器
    tokenScanner.toNext
    ' 4. 将 `result` 包装为结果对象
    Set result = SqlFormatterHelper.wrapperElemToContinuousStrElem(result)
    ' 5. 将 `token` 保存到 `result`
    result.addComponent token
    ' 6. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
    If Not tokenScanner.hasNext Then
        Set parseColFromStrName = result
        Exit Function
    End If

    ' ==================================================
    ' 4. 尝试处理 tableName.colNameVal 中的 colNameVal
    ' ==================================================
    ' 1. 从扫描器中获取一个 token (第三个 token) 到 `token`
    Set token = tokenScanner.peek
    ' 2. 检查: 如果 token 类型是 `SqlTokenType.STR_NAME` 或者 `SqlTokenType.ASTERISK`，则继续
    If token.tokenType = SqlTokenType.STR_NAME Or token.tokenType = SqlTokenType.ASTERISK Then
        ' 2.1 向前推进扫描器
        tokenScanner.toNext
        ' 2.2 将 `token` 保存到 `result`
        result.addComponent token
    End If

    ' ==================================================
    ' 5. 对 `result` 执行: 列对象括号配对，结果保存到 `result`
    ' ==================================================
    Set parseColFromStrName = matchColBoundaryParenthesis(result, tokenScanner, leftParenthesisStack)

    ' ==================================================
    ' 6. 退出处理，并返回 `result`
    ' ==================================================
    ' return result
End Function

' 解析连续出现的多个表定义
'
' @param tokenScanner 扫描器
' @return　逗号分隔的多个元素对象
Public Function parseTableReferences(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 快速结束
    ' ==================================================
    ' 1. 检查: 如果扫描器已经用完，则退出处理，并返回 null
    If Not tokenScanner.hasNext Then
        ' return null
        Exit Function
    End If

    ' ==================================================
    ' 2. 声明变量
    ' ==================================================
    ' 1. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 parseTableReferences
    ' 2. 表定义对象 `ISqlFormatterElem tempElem`
    Dim tempElem As ISqlFormatterElem
    ' 3. token `SqlToken token`
    Dim token As SqlToken
    ' 4. tokenType `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType
    ' 5. 偏移量 `int offset`
    Dim offset As Integer

    ' ==================================================
    ' 3. 匹配第一个表定义
    ' ==================================================
    ' ==============================
    ' 3.1. 吸收列开始前的注释
    ' ==============================
    ' 1. 执行: 获取扫描器到下一个最近的表定义的偏移量，结果保存到 `offset`
    offset = SqlFormatterHelper.getNextTableFactorOffset(tokenScanner)
    ' 2. 检查: 如果不存在筛选项 `offset == 0`，退出处理，并返回 null
    If offset = 0 Then
        ' return null
        Exit Function

    ' 3. 检查: 如果 `offset > 1`
    ElseIf offset > 1 Then
        ' 3.1. 创建结果对象 `result`
        Set parseTableReferences = SqlFormatterHelper.createCommaDelimitMultiElem(True)
        ' 3.2. 执行:吸收 offset - 1 个注释元素到 result 右侧
        consumeComponentToEnvRight parseTableReferences, tokenScanner, offset - 1
    End If

    ' ==============================
    ' 3.2. 匹配表定义
    ' ==============================
    ' 1. 执行: 匹配一个表定义，结果保存到 `tempElem`
    Set tempElem = parseTableFactor(tokenScanner)
    ' 2. 检查: 如果 `result` 不是空，则将 `tempElem` 保存到 `result`
    If Not parseTableReferences Is Nothing Then
        parseTableReferences.addComponent tempElem
    End If
    ' 3. 检查: 如果扫描器已经用完，则退出处理
    If Not tokenScanner.hasNext Then
        ' 3.1. 检查: 如果 `result` 不是空，则返回 `result`
        If Not parseTableReferences Is Nothing Then
            ' return result
            Exit Function

        ' 3.2. 否则，返回 `tempElem`
        Else
            ' return tempElem
            Set parseTableReferences = tempElem
            Exit Function
        End If
    End If

    ' ==================================================
    ' 4. 检查是否是多个
    ' ==================================================
    ' 1. 获取扫描器到下一个最近的逗号的偏移量，结果保存到 `offset`
    offset = SqlFormatterHelper.getNextCommaOffset(tokenScanner)
    ' 2. 检查: 如果 `offset == 0`，则退出处理
    If offset = 0 Then
        ' 2.1. 检查: 如果 `result` 不是空，则返回 `result`
        If Not parseTableReferences Is Nothing Then
            ' return result
            Exit Function

        ' 2.2. 否则，返回 `tempElem`
        Else
            ' return tempElem
            Set parseTableReferences = tempElem
            Exit Function
        End If
    End If

    ' 3. 检查: 如果 `result` 是空
    If parseTableReferences Is Nothing Then
        ' 3.1. 创建结果对象 `result`
        Set parseTableReferences = SqlFormatterHelper.createCommaDelimitMultiElem(True)
        ' 3.2. 将 `tempElem` 保存到 `result`
        parseTableReferences.addComponent tempElem
    End If

    ' 4. 检查: 如果 `offset > 1`，执行:吸收 offset - 1 个注释元素到 result 右侧
    If offset > 1 Then
        consumeComponentToEnvRight parseTableReferences, tokenScanner, offset - 1
    End If

    ' 5. 弹出一个 `token` (这里是逗号)，将 `token` 保存到 `result`
    parseTableReferences.addComponent tokenScanner.getNext

    ' 6. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If

    ' 7. 吸收同行注释
    ' 7.1. 从扫描器中获取一个 `token`
    Set token = tokenScanner.peek
    ' 7.2. 检查: 如果 `token` 是一个同行注释
    If token.ISqlFormatterElem_isShareLineComment Then
        ' 7.2.1. 向前推进扫描器
        tokenScanner.toNext
        ' 7.2.2. 将 `token` 保存到 `result`
        parseTableReferences.addComponent token
    End If

    ' ==================================================
    ' 5. 遍历，收集剩余的表定义
    ' ==================================================
    ' 5.1. 遍历扫描器，如果扫描器用完了，则退出遍历
    While tokenScanner.hasNext
        ' ==============================
        ' 5.2. 吸收列开始前的注释
        ' ==============================
        ' 1. 执行: 获取扫描器到下一个最近的表定义的偏移量，结果保存到 `offset`
        offset = SqlFormatterHelper.getNextTableFactorOffset(tokenScanner)
        ' 2. 检查: 如果不存在筛选项 `offset == 0`，退出遍历
        If offset = 0 Then
            GoTo whileBreak

        ' 3. 否则，检查: 如果 `offset > 1`，执行: 吸收 offset - 1 个注释元素到 result 右侧
        ElseIf offset > 1 Then
            consumeComponentToEnvRight parseTableReferences, tokenScanner, offset - 1
        End If

        ' ==============================
        ' 5.3. 匹配表定义
        ' ==============================
        ' 1. 执行: 匹配一个表定义，结果保存到 `tempElem`
        Set tempElem = parseTableFactor(tokenScanner)

        ' 2. 将 `tempElem` 保存到 `result`
        parseTableReferences.addComponent tempElem

        ' 3. 检查: 如果扫描器已经用完，则退出遍历
        If Not tokenScanner.hasNext Then
            GoTo whileBreak
        End If

        ' ==============================
        ' 5.4. 处理逗号
        ' ==============================
        ' 1. 获取扫描器到下一个最近的逗号的偏移量，结果保存到 `offset`
        offset = SqlFormatterHelper.getNextCommaOffset(tokenScanner)
        ' 2. 检查: 如果 `offset == 0`，则退出处理
        If offset = 0 Then
            GoTo whileBreak

        ' 3. 否则，检查: 如果 `offset > 1`，执行:吸收 offset - 1 个注释元素到 result 右侧
        ElseIf offset > 1 Then
            consumeComponentToEnvRight parseTableReferences, tokenScanner, offset - 1
        End If
        ' 4. 弹出一个 `token` (这里是逗号)，将 `token` 保存到 `result`
        parseTableReferences.addComponent tokenScanner.getNext
        ' 5. 检查: 如果扫描器已经用完，则退出遍历
        If Not tokenScanner.hasNext Then
            GoTo whileBreak
        End If

        ' 6. 吸收同行注释
        ' 6.1. 从扫描器中获取一个 `token`
        Set token = tokenScanner.peek
        ' 6.2. 检查: 如果 `token` 是一个同行注释
        If token.ISqlFormatterElem_isShareLineComment Then
            ' 6.2.1. 向前推进扫描器
            tokenScanner.toNext
            ' 6.2.2. 将 `token` 保存到 `result`
            parseTableReferences.addComponent token
        End If

        ' ==============================
        ' 5.5. 从 2 开始进入下一次遍历
        ' ==============================
    Wend
whileBreak:

    ' ==================================================
    ' 6. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 匹配一个表定义
'
' @param tokenScanner 扫描器
' @return 表定义对象 SqlFormatterTableFactorEnv
Public Function parseTableFactor(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. token `SqlToken token`
    Dim token As SqlToken
    ' 2. tokenType `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType

    ' ==================================================
    ' 2. 从扫描器中获取一个token，并保存其类型到 `tokenType`
    ' ==================================================
    Set token = tokenScanner.peek
    tokenType = token.tokenType

    ' ==================================================
    ' 3. 检查: 如果 tokenType 是一个名称: `SqlTokenType.STR_NAME`
    ' ==================================================
    If tokenType = SqlTokenType.STR_NAME Then
        ' 1. 执行: 匹配一个表名称定义开始的表定义，并返回执行结果
        Set parseTableFactor = parseTableFactorFromTableName(tokenScanner)

    ' ==================================================
    ' 4. 否则，检查: 如果 tokenType 是一个左括号: `SqlTokenType.LEFT_PARENTHESIS`
    ' ==================================================
    ElseIf tokenType = SqlTokenType.LEFT_PARENTHESIS Then
        ' 1. 执行: 匹配一个子查询开始的表定义，并返回执行结果
        Set parseTableFactor = parseTableFactorFromSubQuery(tokenScanner)

    ' ==================================================
    ' 5. 否则，检查: 如果 tokenType 是一个 with: `SqlTokenType.KEY_WITH`
    ' ==================================================
    ElseIf tokenType = SqlTokenType.KEY_WITH Then
        ' 1. 执行: 匹配一个子查询开始的表定义，并返回执行结果
        Set parseTableFactor = parseTableFactorFromSubQuery(tokenScanner)

    ' ==================================================
    ' 6. 否则，检查: 如果 tokenType 是一个 select: `SqlTokenType.KEY_SELECT`
    ' ==================================================
    ElseIf tokenType = SqlTokenType.KEY_SELECT Then
        ' 1. 执行: 匹配一个子查询开始的表定义，并返回执行结果
        Set parseTableFactor = parseTableFactorFromSubQuery(tokenScanner)

    ' ==================================================
    ' 7. 否则，退出处理，并返回 null
    ' ==================================================
    ' else {
    '     return null
    End If
End Function

' 匹配一个表名称定义开始的表定义
'
' @param tokenScanner 扫描器
' @return 表定义作用域 SqlFormatterTableFactorEnv
Public Function parseTableFactorFromTableName(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 表定义 `ISqlFormatterElem tempElem`
    Dim tempElem As ISqlFormatterElem
    ' 2. 表定义 `int elemOffset`
    Dim elemOffset As Integer
    ' 3. 表定义作用域 `SqlFormatterTableFactorEnv result`
    Dim result As SqlFormatterTableFactorEnv
    ' 4. 遍历索引 `int i`
    Dim i As Integer
    ' 5. token `SqlToken token`
    Dim token As SqlToken

    ' ==================================================
    ' 2. 匹配表定义
    ' ==================================================
    ' 1. 执行: [匹配一个表名称定义](#匹配一个表名称定义)，结果保存到 `tempElem`
    Set tempElem = parseTableName(tokenScanner)
    ' 2. 检查: 如果扫描器已经用完，则退出处理，并返回 `tempElem`
    If Not tokenScanner.hasNext Then
        ' return tempElem
        Set parseTableFactorFromTableName = tempElem
        Exit Function
    End If

    ' ==================================================
    ' 3. 匹配 partition
    ' ==================================================
    ' ==============================
    ' 3.1. 检查是否存在 partition
    ' ==============================
    ' 1. 执行: 获取扫描器到下一个最近的目标元素的偏移量，结果保存到 `elemOffset`
    elemOffset = SqlFormatterHelper.getNextTargetElemOffset(tokenScanner, SqlTokenType.KEY_PARTITION)
    ' 2. 检查: 如果 `elemOffset != 0`，则继续这一节的后续处理
    If elemOffset <> 0 Then
        ' ==============================
        ' 3.2. 初始化 `result`
        ' ==============================
        ' 1. 创建 `result = new SqlFormatterTableFactorEnv()`
        Set result = New SqlFormatterTableFactorEnv

        ' 2. 将 `tempElem` 保存到 `result`
        result.ISqlFormatterElem_addComponent tempElem

        ' ==============================
        ' 3.3. 吸收注释
        ' ==============================
        ' 1. 检查: 如果 `elemOffset > 1`，则继续
        If elemOffset > 1 Then
            ' 2. 从 `i = 1` 开始到 `elemOffset - 1` 遍历
            For i = 1 To elemOffset - 1
                ' 3. 从扫描器中弹出一个 `token`
                Set token = tokenScanner.getNext
                ' 4. 将 `token` 保存到 `result`
                result.ISqlFormatterElem_addComponent token
                ' 5. 从 3 开始下一轮遍历
            Next
        End If

        ' ==============================
        ' 3.4. 执行: 匹配partition，结果保存到 `tempElem`
        ' ==============================
        Set tempElem = parsePartition(tokenScanner)

        ' ==============================
        ' 3.5. 将 `tempElem` 保存到 `result`
        ' ==============================
        result.setPartition tempElem

        ' ==============================
        ' 3.6. 设置 `tempElem` 为 null
        ' ==============================
        ' 防止后面处理 index_hint_list 时，重复保存 `tempElem`
        Set tempElem = Nothing

        ' ==============================
        ' 3.7. 吸收注释
        ' ==============================
        ' 1. 遍历扫描器，如果扫描器用完，则退出遍历
        While tokenScanner.hasNext
            ' 2. 从扫描器中获取一个 `token`
            Set token = tokenScanner.peek
            ' 3. 检查: 如果 `token` 类型不是注释，则退出遍历: `ISqlFormatterElem_isComment() == false`
            If token.ISqlFormatterElem_isComment = False Then
                GoTo beforePartitionCommentBreak
            End If
            ' 4. 将 `token` 保存到 `result`
            result.ISqlFormatterElem_addComponent token
            ' 5. 扫描器向前推进
            tokenScanner.toNext
            ' 6. 从 2 开始下一轮遍历
        Wend
beforePartitionCommentBreak:

        ' ==============================
        ' 3.8. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
        ' ==============================
        If Not tokenScanner.hasNext Then
            ' return result
            Set parseTableFactorFromTableName = result
            Exit Function
        End If
    End If

    ' ==================================================
    ' 4. 尝试匹配 As
    ' ==================================================
    ' 1. 从扫描器中获取一个 `token`
    Set token = tokenScanner.peek
    ' 2. 检查: 如果 token 的类型是 `SqlTokenType.KEY_AS`，则继续
    If token.tokenType = SqlTokenType.KEY_AS Then
        ' 3. 扫描器向前推进
        tokenScanner.toNext
        ' 4. 检查: 如果 `result` 是空
        If result Is Nothing Then
            ' 4.1. 创建表定义作用域 `result = new SqlFormatterTableFactorEnv()`
            Set result = New SqlFormatterTableFactorEnv
            ' 4.2. 将 `tempElem` 保存到 `result`
            result.ISqlFormatterElem_addComponent tempElem
        End If
        ' 5. 将 `token` 保存到 `result`
        result.ISqlFormatterElem_addComponent token

        ' 6. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
        If Not tokenScanner.hasNext Then
            ' return result
            Set parseTableFactorFromTableName = result
            Exit Function
        End If
        ' 7. 从扫描器中获取一个 `token`
        Set token = tokenScanner.peek
    End If

    ' ==================================================
    ' 5. 尝试匹配 alias (如果不存在，则退出)
    ' ==================================================
    ' 1. 检查: 如果 token 类型是 `SqlTokenType.STR_NAME`，则继续
    If token.tokenType = SqlTokenType.STR_NAME Then
        ' 2. 扫描器向前推进
        tokenScanner.toNext

        ' 3. 检查: 如果 `result` 是空，则创建
        If result Is Nothing Then
            ' 3.1. 创建表定义作用域 `result = new SqlFormatterTableFactorEnv()`
            Set result = New SqlFormatterTableFactorEnv
            ' 3.2. 将 `tempElem` 保存到 `result`: `ISqlFormatterElem_addComponent`
            result.ISqlFormatterElem_addComponent tempElem
        End If

        ' 4. 将 `token` 保存到 `result`
        result.ISqlFormatterElem_addComponent token

        ' 5. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
        If Not tokenScanner.hasNext Then
            ' return result
            Set parseTableFactorFromTableName = result
            Exit Function
        End If
    End If

    ' ==================================================
    ' 6. 尝试匹配 index_hint_list
    ' ==================================================
    ' ==============================
    ' 6.1. 检查是否存在 index_hint
    ' ==============================
    ' 1. 执行: 获取扫描器到下一个最近的index_hint起始关键字的偏移量，结果保存到 `elemOffset`
    elemOffset = SqlFormatterHelper.getNextIndexHintStartKeyOffset(tokenScanner)

    ' 2. 检查: 如果 `elemOffset != 0`，则继续这一节的后续处理
    If elemOffset <> 0 Then
        ' ==============================
        ' 6.2. 检查: 如果 `result` 是空，则创建
        ' ==============================
        If result Is Nothing Then
            ' 1. 创建表定义作用域 `result = new SqlFormatterTableFactorEnv()`
            Set result = New SqlFormatterTableFactorEnv
            ' 2. 将 `tempElem` 保存到 `result`
            result.ISqlFormatterElem_addComponent tempElem
        End If

        ' ==============================
        ' 6.3. 吸收注释
        ' ==============================
        ' 1. 检查: 如果 `elemOffset > 1`，则继续
        If elemOffset > 1 Then
            ' 2. 从 `i = 1` 开始到 `elemOffset - 1` 遍历
            For i = 1 To elemOffset - 1
                ' 3. 从扫描器中弹出一个 `token`
                Set token = tokenScanner.getNext
                ' 4. 将 `token` 保存到 `result`
                result.ISqlFormatterElem_addComponent token
                ' 5. 从 3 开始下一轮遍历
            Next
        End If

        ' ==============================
        ' 6.4. 执行: 匹配index_hint_list，结果保存到 `tempElem`
        ' ==============================
        Set tempElem = parseIndexHintList(tokenScanner)

        ' ==============================
        ' 6.5. 将 `tempElem` 保存到 `result`
        ' ==============================
        result.setIndexHintList tempElem
    End If

    ' ==================================================
    ' 7. 退出处理
    ' ==================================================
    ' 1. 检查: 如果 `result` 不是空，则返回 `result`
    If Not result Is Nothing Then
        ' 1.1. 吸收一个同行注释到 result 右侧，并返回执行结果
        Set parseTableFactorFromTableName = consumeOneShareLineCommentToEnvRight(result, tokenScanner) 

    ' 2. 否则，返回 `tempElem`
    Else
        ' 2.1. 吸收一个同行注释到 tempElem 右侧，并返回执行结果
        Set parseTableFactorFromTableName = consumeOneShareLineCommentToEnvRight(tempElem, tokenScanner) 
    End If
End Function

' 匹配 partition
'
' @param tokenScanner 扫描器
' @return partition作用域 SqlFormatterContainerCol
Public Function parsePartition(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. token `SqlToken token`
    Dim token As SqlToken
    ' 2. tokenType `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType
    ' 3. 结果对象 `SqlFormatterContainerCol result`
    Dim result As SqlFormatterContainerCol

    ' ==================================================
    ' 2. 处理 partition
    ' ==================================================
    ' 1. 从扫描器中弹出一个 `token`
    Set token = tokenScanner.getNext
    ' 2. 检查: 如果扫描器已经用完，则退出处理，并返回 `token`
    If Not tokenScanner.hasNext Then
        ' return token
        Set parsePartition = token
        Exit Function
    End If

    ' 3. 创建结果对象 `result = new SqlFormatterContainerCol()`
    Set result = New SqlFormatterContainerCol
    ' 4. 将 `token` 保存到 `result`
    result.ISqlFormatterElem_addComponent token

    ' ==================================================
    ' 3. 吸收注释
    ' ==================================================
    ' 1. 遍历扫描器，如果扫描器已经用完，则退出遍历
    While tokenScanner.hasNext
        ' 2. 从扫描器中获取一个 `token`
        Set token = tokenScanner.peek
        ' 3. 检查: 如果 token 类型不是注释，则退出遍历: `ISqlFormatterElem_isComment() == false`
        If token.ISqlFormatterElem_isComment = False Then
            GoTo consumeCommentBreak
        End If
        ' 4. 将 `token` 保存到 `result`
        result.ISqlFormatterElem_addComponent token
        ' 5. 向前推进扫描器
        tokenScanner.toNext
        ' 6. 从 2 开始下一轮遍历
    Wend
consumeCommentBreak:

    ' ==================================================
    ' 4. 处理左括号
    ' ==================================================
    ' 1. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Set parsePartition = result
        Exit Function
    End If

    ' 2. 从扫描器中获取一个 `token`
    Set token = tokenScanner.peek
    ' 3. 检查: 如果 token 的类型不是左括号，则退出处理并返回 `result`
    If token.tokenType <> SqlTokenType.LEFT_PARENTHESIS Then
        ' return result
        Set parsePartition = result
        Exit Function
    End If
    ' 4. 向前推进扫描器
    tokenScanner.toNext
    ' 5. 将 `token` 保存到 `result`
    result.ISqlFormatterElem_addComponent token

    ' ==================================================
    ' 5. 吸收注释 + 逗号 + 名称
    ' ==================================================
    ' 1. 遍历扫描器，如果扫描器已经用完，则退出遍历
    While tokenScanner.hasNext
        ' 2. 从扫描器中获取一个 `token`，并保存 token 的类型到 `tokenType`
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' 3. 检查: 如果 token 的类型是注释，则继续
        If SqlHelper.isCommentToken(tokenType) Then
            ' noop

        ' 4. 否则，检查: 如果 token 的类型是逗号，则继续
        ElseIf tokenType = SqlTokenType.COMMA Then
            ' noop

        ' 5. 否则，检查: 如果 token 的类型是名称: `SqlTokenType.STR_NAME`，则继续
        ElseIf tokenType = SqlTokenType.STR_NAME Then
            ' noop

        ' 6. 否则，退出遍历
        Else
            GoTo consumeElemBreak
        End If
        ' 7. 将 `token` 保存到 `result`
        result.ISqlFormatterElem_addComponent token
        ' 8. 向前推进扫描器
        tokenScanner.toNext
        ' 9. 从 2 开始下一轮遍历
    Wend
consumeElemBreak:

    ' ==================================================
    ' 6. 处理右括号
    ' ==================================================
    ' 1. 检查: 如果扫描器没用完，则继续
    If tokenScanner.hasNext Then
        ' 2. 从扫描器中获取一个 `token`
        Set token = tokenScanner.peek
        ' 3. 检查: 如果 token 的类型是右括号，则继续
        If token.tokenType = SqlTokenType.RIGHT_PARENTHESIS Then
            ' 4. 将 `token` 保存到 `result`
            result.ISqlFormatterElem_addComponent token
            ' 5. 向前推进扫描器
            tokenScanner.toNext
        End If
    End If

    ' ==================================================
    ' 7. 退出处理，返回 `result`
    ' ==================================================
    ' return result
    Set parsePartition = result
End Function

' 匹配 index hint list
'
' @param tokenScanner 扫描器
' @return index hint list 作用域对象 SqlFormatterCommaDeliMultiElem
Public Function parseIndexHintList(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 临时元素 `ISqlFormatterElem tempElem`
    Dim tempElem As ISqlFormatterElem
    ' 2. token `SqlToken token`
    Dim token As SqlToken
    ' 3. tokenType `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType
    ' 4. 逗号的 token `SqlToken commaToken`
    Dim commaToken As SqlToken
    ' 5. index_hint_list: `SqlFormatterCommaDeliMultiElem indexHintList`
    Dim indexHintList As SqlFormatterCommaDeliMultiElem
    ' 6. 元素偏移量: `int elemOffset`
    Dim elemOffset As Integer
    ' 7. 标记，是否存在逗号: `boolean hasComma = false`
    Dim hasComma As Boolean

    ' ==================================================
    ' 2. 尝试解析第一个 index_hint
    ' ==================================================
    ' 1. 执行: 匹配index_hint，结果保存到 `tempElem`
    Set tempElem = parseIndexHint(tokenScanner)
    ' 2. 检查: 如果扫描器已经用完，则退出处理，返回 `tempElem`
    If Not tokenScanner.hasNext Then
        ' return tempElem
        Set parseIndexHintList = tempElem
        Exit Function
    End If

    ' ==================================================
    ' 3. 检查是否是多个 (这里也只是可能存在)
    ' ==================================================
    ' 1. 获取扫描器到下一个最近的逗号的偏移量，并检查: 如果结果是 0，则退出处理，并返回 `tempElem`
    If SqlFormatterHelper.getNextCommaOffset(tokenScanner) = 0 Then
        ' return tempElem
        Set parseIndexHintList = tempElem
        Exit Function
    End If

    ' ==================================================
    ' 4. 遍历，收集剩余的 index_hint
    ' ==================================================
    ' ==============================
    ' 4.1. 初始化变量: 是否遇到了逗号: `hasComma = false`
    ' ==============================
    hasComma = False

    ' ==============================
    ' 4.2. 遍历扫描器，如果扫描器用完了，则退出遍历
    ' ==============================
    While tokenScanner.hasNext
        ' ==============================
        ' 4.3. 从扫描器中获取一个 `token`，并保存 `tokenType = token.tokenType`
        ' ==============================
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' ==============================
        ' 4.4 检查，如果 tokenType 是逗号: `SqlTokenType.COMMA`
        ' ==============================
        If tokenType = SqlTokenType.COMMA Then
            ' 1. 向前推进扫描器
            tokenScanner.toNext

            ' 2. 检查: 如果扫描器已经用完
            If Not tokenScanner.hasNext Then
                ' 2.1. 检查: 如果 `indexHintList` 是空
                If indexHintList Is Nothing Then
                    ' 2.1.1. 创建 `indexHintList = new SqlFormatterCommaDeliMultiElem`
                    Set indexHintList = New SqlFormatterCommaDeliMultiElem
                    ' 2.1.2. 将 `tempElem` 保存 `indexHintList`
                    indexHintList.ISqlFormatterElem_addComponent tempElem
                End If
                ' 2.2. 将 `token` 保存到 `indexHintList`
                indexHintList.ISqlFormatterElem_addComponent token
                ' 2.3. 退出遍历
                GoTo whileBreak
            End If

            ' 3. 执行: 获取扫描器到下一个最近的index_hint起始关键字的偏移量，结果保存到 `elemOffset`
            elemOffset = SqlFormatterHelper.getNextIndexHintStartKeyOffset(tokenScanner)

            ' 4. 检查: 如果关键字偏移量是 0
            If elemOffset = 0 Then
                ' 4.1. 将当前逗号的 token 写回扫描器: `tokenScanner.back(1)`
                tokenScanner.back 1
                ' 4.2. 退出遍历
                GoTo whileBreak
            End If

            ' 5. 检查: 如果 `indexHintList` 是空
            If indexHintList Is Nothing Then
                ' 5.1. 创建 `indexHintList = new SqlFormatterCommaDeliMultiElem`
                Set indexHintList = New SqlFormatterCommaDeliMultiElem
                ' 5.2. 将 `tempElem` 保存 `indexHintList`
                indexHintList.ISqlFormatterElem_addComponent tempElem
            End If

            ' 6. 将 `token` 保存到 `indexHintList`
            indexHintList.ISqlFormatterElem_addComponent token
            ' 7. 设置 `hasComma = true`
            hasComma = True

        ' ==============================
        ' 4.5. 否则，检查: 如果 tokenType 是注释: `SqlHelper.isCommentToken`
        ' ==============================
        ElseIf SqlHelper.isCommentToken(tokenType) Then
            ' 1. 向前推进扫描器
            tokenScanner.toNext
            ' 2. 检查: 如果 `indexHintList` 是空
            If indexHintList Is Nothing Then
                ' 2.1. 创建 `indexHintList = new SqlFormatterCommaDeliMultiElem`
                Set indexHintList = New SqlFormatterCommaDeliMultiElem
                ' 2.2. 将 `tempElem` 保存 `indexHintList`: `ISqlFormatterElem_addComponent`
                indexHintList.ISqlFormatterElem_addComponent tempElem
            End If
            ' 3. `token` 保存到 `indexHintList`: `ISqlFormatterElem_addComponent`
            indexHintList.ISqlFormatterElem_addComponent token

        ' ==============================
        ' 4.6. 否则，检查: 如果 `hasComma == false`，则退出遍历
        ' ==============================
        ElseIf hasComma = False Then
            ' 这里表示遇到了当前作用域的结束位置
            GoTo whileBreak

        ' ==============================
        ' 4.7. 否则，匹配一个 index_hint
        ' ==============================
        Else
            ' 1. 检查: 如果 `indexHintList` 是空
            If indexHintList Is Nothing Then
                ' 1.1. 创建 `indexHintList = new SqlFormatterCommaDeliMultiElem`
                Set indexHintList = New SqlFormatterCommaDeliMultiElem
                ' 1.2. 将 `tempElem` 保存 `indexHintList`: `ISqlFormatterElem_addComponent`
                indexHintList.ISqlFormatterElem_addComponent tempElem
            End If
            ' 2. 执行: [匹配index_hint](#匹配index_hint)，结果保存到 `tempElem`
            Set tempElem = parseIndexHint(tokenScanner)
            ' 3. `tempElem` 保存到 `indexHintList`: `ISqlFormatterElem_addComponent`
            indexHintList.ISqlFormatterElem_addComponent tempElem
            ' 4. 重置: `hasComma = false`
            hasComma = False
        End If

        ' ==============================
        ' 4.8. 从 3 开始进入下一次遍历
        ' ==============================
    Wend
whileBreak:

    ' ==================================================
    ' 5. 退出处理
    ' ==================================================
    ' 1. 检查: 如果 `indexHintList` 是空，则返回 `tempElem`
    If indexHintList Is Nothing Then
        Set parseIndexHintList = tempElem

    ' 2. 否则
    Else
        ' 2.1. 设置 `indexHintList` 遇到逗号时换行: `setNeedMultiLineWhenHasComma(true)`
        indexHintList.setNeedMultiLineWhenHasComma True
        ' 2.2. 返回 `indexHintList`
        Set parseIndexHintList = indexHintList
    End If
End Function

' 匹配index_hint
'
' @param tokenScanner 扫描器
' @return index_hint对象 SqlFormatterKeySectionEnv
Public Function parseIndexHint(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. key `SqlFormatterTokenGroup key`
    Dim key As SqlFormatterTokenGroup
    ' 2. token `SqlToken firstToken`
    Dim firstToken As SqlToken
    ' 3. token `SqlToken token`
    Dim token As SqlToken
    ' 4. tokenType `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType
    ' 5. 容器对象 `SqlFormatterContainerCol container`
    Dim container As SqlFormatterContainerCol

    ' ==================================================
    ' 2. 处理 key 部分
    ' ==================================================
    ' ==============================
    ' 2.1. 处理第一个关键字
    ' ==============================
    ' 1. 从扫描器中弹出一个 token，保存到 `firstToken`
    Set firstToken = tokenScanner.getNext
    ' 2. 检查: 如果扫描器已经用完，则退出处理，并返回 `firstToken`
    If Not tokenScanner.hasNext Then
        ' return firstToken
        Set parseIndexHint = firstToken
        Exit Function
    End If

    ' ==============================
    ' 2.2. 处理第二个关键字
    ' ==============================
    ' 1. 从扫描器中获取一个 `token`，并保存 token 类型到 `tokenType`
    Set token = tokenScanner.peek
    tokenType = token.tokenType

    ' 2. 检查: 如果 `tokenType` 不是 `SqlTokenType.KEY_INDEX` + `SqlTokenType.KEY_KEY`，则退出处理，并返回 `firstToken`
    If tokenType <> SqlTokenType.KEY_INDEX And tokenType <> SqlTokenType.KEY_KEY Then
        ' return firstToken
        Set parseIndexHint = firstToken
        Exit Function
    End If

    ' 3. 向前推进扫描器
    tokenScanner.toNext

    ' ==============================
    ' 2.3. 生成 key
    ' ==============================
    ' 1. 创建 key : `key = new SqlFormatterTokenGroup()`
    Set key = New SqlFormatterTokenGroup
    ' 2. 将 `firstToken` 保存到 `key`
    key.ISqlFormatterElem_addComponent firstToken
    ' 3. 将 `token` 保存到 `key`
    key.ISqlFormatterElem_addComponent token

    ' ==============================
    ' 2.4. 检查: 如果扫描器已经用完，则退出处理，并返回 `key`
    ' ==============================
    If Not tokenScanner.hasNext Then
        ' return key
        Set parseIndexHint = key
        Exit Function
    End If

    ' ==================================================
    ' 3. 尝试匹配 [FOR {JOIN|ORDER BY|GROUP BY}]
    ' ==================================================
    ' 1. 从扫描器中获取一个 token，保存到 `firstToken`
    Set firstToken = tokenScanner.peek

    ' 2. 检查: 如果 firstToken 的类型是 `SqlTokenType.KEY_FOR`，则继续
    If firstToken.tokenType = SqlTokenType.KEY_FOR Then
        ' 3. 向前推进扫描器
        tokenScanner.toNext

        ' 4. 检查，如果扫描器已经用完
        If Not tokenScanner.hasNext Then
            ' 4.1. 生成一个KeySectionEnv，并返回执行结果: `SqlFormatterHelper.createKeySectionEnv`
            Set parseIndexHint = SqlFormatterHelper.createKeySectionEnv(key, firstToken)
            Exit Function
        End If

        ' 5. 从扫描器中获取一个 `token`，并保存元素类型到 `tokenType`
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' 6. 检查: 如果 token 的类型不是: `SqlTokenType.KEY_JOIN` + `SqlTokenType.KEY_ORDER_BY` + `SqlTokenType.KEY_GROUP_BY`
        If tokenType <> SqlTokenType.KEY_JOIN _
            And tokenType <> SqlTokenType.KEY_ORDER_BY _
            And tokenType <> SqlTokenType.KEY_GROUP_BY Then

            ' 6.1. 生成一个KeySectionEnv，并返回执行结果
            Set parseIndexHint = SqlFormatterHelper.createKeySectionEnv(key, firstToken)
            Exit Function
        End If

        ' 7. 向前推进扫描器
        tokenScanner.toNext

        ' 8. 保存这两个 key
        ' 8.1. 生成容器对象: `container = new SqlFormatterContainerCol()`
        Set container = New SqlFormatterContainerCol
        ' 8.2. 将 `firstToken` 保存到 `container`
        container.ISqlFormatterElem_addComponent firstToken
        ' 8.3. 将 `token` 保存到 `container`
        container.ISqlFormatterElem_addComponent token
        ' 9. 检查: 如果扫描器已经用完
        If Not tokenScanner.hasNext Then
            ' 9.1. 生成一个KeySectionEnv，并返回执行结果
            Set parseIndexHint = SqlFormatterHelper.createKeySectionEnv(key, container)
            Exit Function
        End If
    End If

    ' ==================================================
    ' 4. 解析 (index_list)
    ' ==================================================
    ' 1. 执行: 获取扫描器到下一个最近的左括号的偏移量，并检查: 如果结果是 0
    If SqlFormatterHelper.getNextLeftParenthesisOffset(tokenScanner) = 0 Then
        ' 1.1. 检查: 如果 `container` 是空，则退出处理，返回 `key`
        If container Is Nothing Then
            Set parseIndexHint = key
            Exit Function

        ' 1.2. 否则，生成一个KeySectionEnv，并返回执行结果: `SqlFormatterHelper.createKeySectionEnv`
        Else
            Set parseIndexHint = SqlFormatterHelper.createKeySectionEnv(key, container)
            Exit Function
        End If
    End If

    ' 2. 检查: 如果 `container` 是空，则创建容器对象
    If container Is Nothing then
        Set container = New SqlFormatterContainerCol
    End If

    ' 3. 遍历扫描器，如果扫描器用完，则退出遍历
    While tokenScanner.hasNext
        ' 4. 从扫描器中获取一个 `token`
        Set token = tokenScanner.peek
        ' 5. 检查: 如果 token 类型是右括号
        If token.tokenType = SqlTokenType.RIGHT_PARENTHESIS Then
            ' 5.1. 将 `token` 保存到 `container`
            container.ISqlFormatterElem_addComponent token
            ' 5.2. 向前推进扫描器
            tokenScanner.toNext
            ' 5.3. 退出遍历
            GoTo whileBreak

        ' 6. 否则
        Else
            ' 6.1. 将 `token` 保存到 `container`
            container.ISqlFormatterElem_addComponent token
            ' 6.2. 向前推进扫描器
            tokenScanner.toNext
        End If

        ' 7. 从 4 开始下一轮遍历
    Wend
whileBreak:

    ' ==================================================
    ' 5. 生成一个KeySectionEnv，并返回执行结果
    ' ==================================================
    Set parseIndexHint = SqlFormatterHelper.createKeySectionEnv(key, container)
End Function

' 匹配一个子查询开始的表定义
'
' @param tokenScanner 扫描器
' @return 表定义作用域 SqlFormatterTableFactorEnv
Public Function parseTableFactorFromSubQuery(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 临时元素 `ISqlFormatterElem tempElem`
    Dim tempElem As ISqlFormatterElem
    ' 2. token `SqlToken token`
    Dim token As SqlToken
    ' 3. 表定义作用域 `SqlFormatterTableFactorEnv result`
    Dim result As SqlFormatterTableFactorEnv
    ' 4. 括号偏移量 `int leftParenthesisOffset`
    Dim leftParenthesisOffset As Integer
    ' 5. 遍历索引 `int i`
    Dim i As Integer

    ' ==================================================
    ' 2. 匹配子查询
    ' ==================================================
    ' 1. 执行: 生成sql作用域的基础驱动--剥离左括号，结果保存到 `tempElem`
    Set tempElem = parseSqlEnv(tokenScanner)
    ' 2. 检查: 如果 `tempElem` 是 null，则退出处理，并返回 null
    If tempElem Is Nothing Then
        ' return null
        Exit Function
    End If
    ' 3. 检查: 如果扫描器已经用完，则退出处理，并返回 `tempElem`
    If Not tokenScanner.hasNext Then
        ' return tempElem
        Set parseTableFactorFromSubQuery = tempElem
        Exit Function
    End If

    ' ==================================================
    ' 3. 尝试匹配 As
    ' ==================================================
    ' 1. 从扫描器中获取一个 `token`
    Set token = tokenScanner.peek

    ' 2. 检查: 如果 token 的类型是 `SqlTokenType.KEY_AS`，则继续
    If token.tokenType = SqlTokenType.KEY_AS Then
        ' 3. 扫描器向前推进
        tokenScanner.toNext

        ' 4. 创建 `result`
        ' 4.1. 创建表定义作用域 `result = new SqlFormatterTableFactorEnv()`
        Set result = New SqlFormatterTableFactorEnv
        ' 4.2. 将 `tempElem` 保存到 `result`
        result.ISqlFormatterElem_addComponent tempElem
        ' 4.3. 将 `token` 保存到 `result`
        result.ISqlFormatterElem_addComponent token

        ' 5. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
        If Not tokenScanner.hasNext Then
            ' return result
            Set parseTableFactorFromSubQuery = result
            Exit Function
        End If

        ' 6. 从扫描器中获取一个 `token`
        Set token = tokenScanner.peek
    End If

    ' ==================================================
    ' 4. 尝试匹配 alias (如果不存在，则退出)
    ' ==================================================
    ' 1. 检查: 如果 token 类型不 `SqlTokenType.STR_NAME`，则退出处理
    If token.tokenType <> SqlTokenType.STR_NAME Then
        ' 1.1. 检查: 如果 `result` 不是空，则返回 `result`
        If Not result Is Nothing Then
            ' return result
            Set parseTableFactorFromSubQuery = result
        ' 1.2. 否则，返回 `tempElem`
        Else
            ' return tempElem
            Set parseTableFactorFromSubQuery = tempElem
        End If

        Exit Function
    End If

    ' 2. 扫描器向前推进
    tokenScanner.toNext

    ' 3. 检查: 如果 `result` 是空，则创建
    If result Is Nothing Then
        ' 3.1. 创建表定义作用域 `result = new SqlFormatterTableFactorEnv()`
        Set result = New SqlFormatterTableFactorEnv
        ' 3.2. 将 `tempElem` 保存到 `result`
        result.ISqlFormatterElem_addComponent tempElem
    End If

    ' 4. 将 `token` 保存到 `result`
    result.ISqlFormatterElem_addComponent token

    ' 5. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Set parseTableFactorFromSubQuery = result
        Exit Function
    End If

    ' ==================================================
    ' 5. 确认是否需要匹配 `(col_list)`
    ' ==================================================
    ' 1. 执行: 获取扫描器到下一个最近的左括号的偏移量，结果保存到 `leftParenthesisOffset`
    leftParenthesisOffset = SqlFormatterHelper.getNextLeftParenthesisOffset(tokenScanner)
    ' 2. 检查: 如果 `leftParenthesisOffset == 0`，则退出处理，并返回 `result`
    If leftParenthesisOffset = 0 Then
        ' return result
        Set parseTableFactorFromSubQuery = result
        Exit Function
    End If

    ' ==================================================
    ' 6. 吸收注释
    ' ==================================================
    ' 1. 检查: 如果 `leftParenthesisOffset > 1`，则继续
    If leftParenthesisOffset > 1 Then
        ' 2. 从 `i = 1` 开始到 `leftParenthesisOffset - 1` 遍历
        For i = 1 To leftParenthesisOffset - 1
            ' 3. 从扫描器中弹出一个 `token`
            Set token = tokenScanner.getNext
            ' 4. 将 `token` 保存到 `result`
            result.ISqlFormatterElem_addComponent token
            ' 5. 从 3 开始下一轮遍历
        Next
    End If

    ' ==================================================
    ' 7. 尝试匹配 `(col_list)`
    ' ==================================================
    ' 1. 匹配括号包裹的多个列对象--parseParenthesisWrapperMultiCol，结果保存到 `tempElem`
    Set tempElem = parseParenthesisWrapperMultiCol(tokenScanner)
    ' 2. 将 `tempElem` 保存到 `result`
    result.ISqlFormatterElem_addComponent tempElem

    ' ==================================================
    ' 8. 退出处理，并返回 `result`
    ' ==================================================
    Set parseTableFactorFromSubQuery = result
End Function

' 匹配一个表名称定义
'
' @param tokenScanner 扫描器
' @return 表名称定义 SqlFormatterContinuousStrElem
Public Function parseTableName(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 第一个名称 token `SqlToken firstNameToken`
    Dim firstNameToken As SqlToken
    ' 2. token `SqlToken token`
    Dim token As SqlToken
    ' 3. 复合结果对象 `ISqlFormatterElem result`
    '    这里直接使用 parseTableName

    ' ==================================================
    ' 2. 处理第一个 token
    ' ==================================================
    ' 1. 从扫描器中弹出一个 token，保存到 `firstNameToken`
    Set firstNameToken = tokenScanner.getNext
    ' 2. 检查: 如果扫描器已经用完，则退出处理，并返回 `firstNameToken`
    If Not tokenScanner.hasNext Then
        Set parseTableName = firstNameToken
        Exit Function
    End If

    ' ==================================================
    ' 3. 尝试处理 schemaName.name 中的 `.`
    ' ==================================================
    ' 1. 从扫描器中获取一个 token (第二个 token) 到 `token`
    Set token = tokenScanner.peek
    ' 2. 检查: 如果 token 类型不是 `SqlTokenType.POINT`
    If token.tokenType <> SqlTokenType.POINT Then
        ' 2.1. 退出处理，返回 `firstNameToken`
        Set parseTableName = firstNameToken
        Exit Function
    End If

    ' 3. 向前推进扫描器
    tokenScanner.toNext
    ' 4. 将 `firstNameToken` 包装为结果对象
    Set parseTableName = SqlFormatterHelper.wrapperElemToContinuousStrElem(firstNameToken)
    ' 5. 将 `token` 保存到 `result`
    parseTableName.addComponent token
    ' 6. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If

    ' ==================================================
    ' 4. 尝试处理 schemaName.name 中的 name 或者 name.* 中的 name
    ' ==================================================
    ' 1. 从扫描器中获取一个 token (第三个 token) 到 `token`
    Set token = tokenScanner.peek
    ' 2. 检查: 如果 token 类型不是 `SqlTokenType.STR_NAME` 和 `SqlTokenType.ASTERISK`
    ' 2. 检查: 如果 token 类型不是 `SqlTokenType.STR_NAME`
    If token.tokenType <> SqlTokenType.STR_NAME And token.tokenType <> SqlTokenType.ASTERISK Then
        ' 1. 退出处理，返回 `result`
        ' return result
        Exit Function
    End If
    ' 3. 向前推进扫描器
    tokenScanner.toNext
    ' 4. 将 `token` 保存到 `result`
    parseTableName.addComponent token

    ' ==================================================
    ' 5. 退出处理，并返回 `result`
    ' ==================================================
    ' return result
End Function

' 匹配函数列对象
' 需要匹配的内容:
'      function -> func(funcParams)  <- STR_FUNC 开头
'
' @param tokenScanner 扫描器
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @return 函数列对象
Public Function parseFunction( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem

    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 parseFunction
    ' 2. 函数体 `ISqlFormatterElem funcBody`
    Dim funcBody As ISqlFormatterElem
    ' 3. token `SqlToken token`
    Dim token As SqlToken

    ' ==================================================
    ' 2. 处理函数名 token
    ' ==================================================
    ' 1. 从扫描器中弹出一个 token，保存到 `result`
    Set parseFunction = tokenScanner.getNext
    ' 2. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If

    ' 3. 生成函数对象，结果保存到 `result`
    Set parseFunction = SqlFormatterHelper.createFuncCol(parseFunction)

    ' ==================================================
    ' 3. 处理函数参数
    ' ==================================================
    ' 1. 检查: 如果扫描器没有用完，则继续
    If tokenScanner.hasNext Then
        ' 2. 从扫描器中获取一个 `token`
        Set token = tokenScanner.peek

        ' 3. 检查: 如果 token 类型是左括号，则继续: `SqlTokenType.LEFT_PARENTHESIS`
        If token.tokenType = SqlTokenType.LEFT_PARENTHESIS Then
            ' 4. 执行: 匹配括号包裹的多个列对象，结果保存到 `funcBody`
            Set funcBody = parseParenthesisWrapperMultiCol(tokenScanner)

            ' 5. 将 `funcBody` 保存到 `result`
            parseFunction.addComponent funcBody
        End If
    End If

    ' ==================================================
    ' 4. 括号配对
    ' ==================================================
    ' 1. 对 `result` 执行: 列对象括号配对，结果保存到 `result`
    Set parseFunction = matchColBoundaryParenthesis(parseFunction, tokenScanner, leftParenthesisStack)

    ' ==================================================
    ' 5. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 匹配括号包裹的多个列对象
'
' @param tokenScanner 扫描器
' @optional-param needMultiLineWhenHasComma 遇到逗号时，是否需要换行
'                 默认值=false，不需要换行
' @return 括号包裹的多个列对象 SqlFormatterCommaDeliMultiElem
Public Function parseParenthesisWrapperMultiCol( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    Optional ByVal needMultiLineWhenHasComma As Boolean = False) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 parseParenthesisWrapperMultiCol
    ' 2. token `SqlToken token`
    Dim token As SqlToken
    ' 3. 标记: 是否存在目标元素 `boolean hasComma = false`
    Dim hasComma As Boolean
    ' 4. 函数参数元素 `ISqlFormatterElem funcParam`
    Dim funcParam As ISqlFormatterElem
    ' 5. tokenType `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType

    ' ==================================================
    ' 2. 处理左括号及其后面的注释
    ' ==================================================
    ' 1. 从扫描器中弹出一个 token
    Set token = tokenScanner.getNext
    ' 2. 检查: 如果扫描器已经用完，则退出处理，并返回 `token`
    If Not tokenScanner.hasNext Then
        ' return token
        Set parseParenthesisWrapperMultiCol = token
        Exit Function
    End If

    ' 3. 创建结果对象 `result`
    Set parseParenthesisWrapperMultiCol = SqlFormatterHelper.createCommaDelimitMultiElem(needMultiLineWhenHasComma)
    ' 4. 吸收右侧连续出现的注释到列对象，结果保存到 `result`
    '    在增加括号前执行，以防止其被包装为容器列对象
    Set parseParenthesisWrapperMultiCol = consumeCommentToColRight( _
        tokenScanner, parseParenthesisWrapperMultiCol)
    ' 5. 给 `result` 增加一个左边界括号
    parseParenthesisWrapperMultiCol.addBoundaryLeftParenthesis
    ' 6. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If

    ' ==================================================
    ' 3. 解析一个函数参数
    ' ==================================================
    ' 1. 执行: 匹配任意形式的列表达式--parseMixedColExpr，结果保存到 `funcParam`
    '    遇到逻辑运算符时，不需要停止
    Set funcParam = parseMixedColExpr(tokenScanner)

    ' 2. 将 `funcParam` 保存到 `result`
    parseParenthesisWrapperMultiCol.addComponent funcParam

    ' ==================================================
    ' 4. 遍历，收集函数参数
    ' ==================================================
    ' 1. 初始化变量: 是否遇到了逗号: `hasComma = false`
    hasComma = False
    ' 2. 遍历扫描器，如果扫描器用完了，则退出遍历
    While tokenScanner.hasNext
        ' 3. 从扫描器中获取一个 `token`，并保存 `tokenType = token.tokenType`
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' 4. 检查，如果 tokenType 是逗号
        If tokenType = SqlTokenType.COMMA Then
            ' 4.1. `token` 保存到 `result`
            parseParenthesisWrapperMultiCol.addComponent token
            ' 4.2. 设置 `hasComma = true`
            hasComma = True
            ' 4.3. 向前推进扫描器
            tokenScanner.toNext

        ' 5. 否则，检查: 如果 tokenType 是注释
        ElseIf SqlHelper.isCommentToken(tokenType) Then
            ' 5.1. `token` 保存到 `result`
            parseParenthesisWrapperMultiCol.addComponent token
            ' 5.2. 向前推进扫描器
            tokenScanner.toNext

        ' 6. 否则，检查: 如果 `hasComma == false`，则退出遍历
        ElseIf hasComma = False Then
            ' 这里表示遇到了当前作用域的结束位置
            GoTo consumeParamBreak

        ' 7. 否则，解析一个函数参数
        Else
            ' 7.1. 执行: 匹配任意形式的列表达式--parseMixedColExpr，结果保存到 `funcParam`
            '    遇到逻辑运算符时，不需要停止
            Set funcParam = parseMixedColExpr(tokenScanner)

            ' 7.2. 将 `funcParam` 保存到 `result`
            parseParenthesisWrapperMultiCol.addComponent funcParam
            ' 7.3. 重制 `hasComma = false`
            hasComma = False
        End If

        ' 8. 从 3 开始下一轮遍历
    Wend
consumeParamBreak:

    ' ==================================================
    ' 5. 吸收一个右括号
    ' ==================================================
    ' 1. 遍历扫描器，如果扫描器用完了，则退出遍历
    While tokenScanner.hasNext
        ' 2. 从扫描器中获取一个 `token`，并保存 `tokenType = token.tokenType`
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' 3. 检查，如果 tokenType 是右括号
        If tokenType = SqlTokenType.RIGHT_PARENTHESIS Then
            ' 3.1. 给 `result` 增加一个右括号
            parseParenthesisWrapperMultiCol.addBoundaryRightParenthesis
            ' 3.2. 向前推进扫描器
            tokenScanner.toNext
            ' 3.3. 退出遍历
            GoTo rightParenthesisBreak

        ' 4. 否则，检查: 如果 tokenType 是注释
        ElseIf SqlHelper.isCommentToken(tokenType) Then
            ' 4.1. 将 token 保存到 `result`
            parseParenthesisWrapperMultiCol.addComponent token
            ' 4.2. 向前推进扫描器
            tokenScanner.toNext

        ' 5. 否则，退出遍历
        Else
            GoTo rightParenthesisBreak
        End If

        ' 6. 从 2 开始下一轮遍历
    Wend
rightParenthesisBreak:

    ' ==================================================
    ' 6. 尝试: 吸收一个同行注释到 result 右侧，结果保存到 `result`
    ' ==================================================
    Set parseParenthesisWrapperMultiCol = consumeOneShareLineCommentToColRight(parseParenthesisWrapperMultiCol, tokenScanner)

    ' ==================================================
    ' 7. 退出处理，并返回 `result`
    ' ==================================================
    ' return result
End Function

' 匹配 case 表达式
'
' @param tokenScanner 扫描器
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @return case 表达式的列对象
Public Function parseCaseStatement( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 parseCaseStatement
    ' 2. token `SqlToken token`
    Dim token As SqlToken
    ' 3. 临时元素 `ISqlFormatterElem tempElem`
    Dim tempElem As ISqlFormatterElem
    ' 4. 元素类型 `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType

    ' ==================================================
    ' 2. 初始化
    ' ==================================================
    ' 1. 从扫描器中弹出 `token`
    Set token = tokenScanner.getNext
    ' 2. 检查: 如果扫描器已经用完，则退出处理，并返回 `token`
    If Not tokenScanner.hasNext Then
        ' return token
        Set parseCaseStatement = token
        Exit Function
    End If
    ' 3. 创建结果对象 `result`
    Set parseCaseStatement = SqlFormatterHelper.createCaseCol(token)

    ' ==================================================
    ' 3. 吸收注释
    ' ==================================================
    ' 1. 遍历扫描器，如果扫描器用完了，则退出遍历
    While tokenScanner.hasNext
        ' 2. 从扫描器中获取一个 `token`，并保存 `tokenType = token.tokenType`
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' 3. 检查: 如果 tokenType 不是注释，则退出遍历: `SqlHelper.isCommentToken`
        If Not SqlHelper.isCommentToken(tokenType) Then
            GoTo consumeCommentBreak
        End If
        ' 4. 将 `token` 保存到 `result`
        parseCaseStatement.addComponent token
        ' 5. 扫描器向前推进
        tokenScanner.toNext
        ' 6. 从 2 开始下一轮遍历
    Wend
consumeCommentBreak:

    ' ==================================================
    ' 4. 处理 case 元素后可能存在的列表达式: `[mixedColExpr]`
    ' ==================================================
    ' 1. 检查: 如果扫描器已经用完了，则退出处理，并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If

    ' 2. 执行 [匹配任意形式的列表达式--parseMixedColExpr](#匹配任意形式的列表达式--parseMixedColExpr)，结果保存到 `tempElem`
    Set tempElem = parseMixedColExpr(tokenScanner)

    ' 3. 将 `tempElem` 保存到 `result`
    parseCaseStatement.addComponent tempElem

    ' ==================================================
    ' 5. 遍历，收集 when + else 表达式，以及 end
    ' ==================================================
    ' 1. 遍历扫描器，如果扫描器用完了，则退出遍历
    While tokenScanner.hasNext
        ' 2. 从扫描器中获取一个 `token`，并保存 `tokenType = token.tokenType`
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' 3. 检查: 如果 tokenType 是 when
        If tokenType = SqlTokenType.KEY_WHEN Then
            ' 3.1. 执行: 匹配case中的when表达式--parseCaseWhenStatement，结果保存到 `tempElem`
            Set tempElem = parseCaseWhenStatement(tokenScanner)
            ' 3.2. 将 `tempElem` 保存到 `result`
            parseCaseStatement.addComponent tempElem

        ' 4. 否则，检查: 如果 tokenType 是 else
        ElseIf tokenType = SqlTokenType.KEY_ELSE Then
            ' 4.1. 执行: 匹配case中的else表达式--parseCaseElseStatement，结果保存到 `tempElem`
            Set tempElem = parseCaseElseStatement(tokenScanner)
            ' 4.2. 将 `tempElem` 保存到 `result`
            parseCaseStatement.addComponent tempElem

        ' 5. 否则，检查: 如果 tokenType 是 end
        ElseIf tokenType = SqlTokenType.KEY_END Then
            ' 5.1. 将 `token` 保存到 `result`
            parseCaseStatement.addComponent token
            ' 5.2. 扫描器向前推进
            tokenScanner.toNext
            ' 5.3. 退出遍历
            GoTo consumeSubElemBreak

        ' 6. 否则，检查: 如果 `tokenType` 是注释: `SqlHelper.isCommentToken`
        ElseIf SqlHelper.isCommentToken(tokenType) Then
            ' 6.1. 将 `token` 保存到 `result`
            parseCaseStatement.addComponent token
            ' 6.2. 扫描器向前推进
            tokenScanner.toNext

        ' 7. 否则，退出遍历
        Else
            GoTo consumeSubElemBreak
        End If
    Wend
consumeSubElemBreak:

    ' ==================================================
    ' 6. 括号配对
    ' ==================================================
    ' 1. 对 `result` 执行: [列对象括号配对](#列对象括号配对)，结果保存到 `result`
    Set parseCaseStatement = matchColBoundaryParenthesis(parseCaseStatement, tokenScanner, leftParenthesisStack)

    ' ==================================================
    ' 7. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 匹配case中的when表达式
'
' @param tokenScanner 扫描器
' @return when表达式的列对象
Public Function parseCaseWhenStatement(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `SqlFormatterCaseCol result`
    '    这里直接使用 parseCaseWhenStatement
    ' 2. token `SqlToken token`
    Dim token As SqlToken
    ' 3. 临时元素 `ISqlFormatterElem tempElem`
    Dim tempElem As ISqlFormatterElem
    ' 4. 是否存在 then 元素`boolean hasThen = false`
    Dim hasThen As Boolean
    ' 5. 第一个元素 `SqlToken firstToken`
    Dim firstToken As SqlToken
    ' 6. 元素类型 `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType

    ' ==================================================
    ' 2. 初始化 (处理 when 关键字)
    ' ==================================================
    ' 1. 从扫描器中弹出 `firstToken`
    Set firstToken = tokenScanner.getNext
    ' 2. 检查: 如果扫描器已经用完，则退出处理，并返回 `firstToken`
    If Not tokenScanner.hasNext Then
        ' return firstToken
        Set parseCaseWhenStatement = firstToken
    End If

    ' ==================================================
    ' 3. 吸收注释
    ' ==================================================
    ' 1. 遍历扫描器，如果扫描器用完了，则退出遍历
    While tokenScanner.hasNext
        ' 2. 从扫描器中获取一个 `token`，并保存 `tokenType = token.tokenType`
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' 3. 检查: 如果 tokenType 不是注释，则退出遍历
        If Not SqlHelper.isCommentToken(tokenType) Then
            GoTo consumeCommentBreak
        End If

        ' 4. 检查: 如果 `result` 是空，则创建结果对象 `result`
        If parseCaseWhenStatement Is Nothing Then
            Set parseCaseWhenStatement = SqlFormatterHelper.createCaseCompositionElem(firstToken)
        End If

        ' 5. 将 `token` 保存到 `result`
        parseCaseWhenStatement.addComponent token

        ' 6. 扫描器向前推进
        tokenScanner.toNext
        ' 7. 从 2 开始下一轮遍历
    Wend
consumeCommentBreak:

    ' ==================================================
    ' 4. 匹配一个 mixedColExpr
    ' ==================================================
    ' 1. 检查: 如果扫描器已经用完，则退出处理，返回 `result`
    If Not tokenScanner.hasNext Then
        ' 1.1. 检查: 如果 `result` 是空，则返回 `firstToken`
        If parseCaseWhenStatement Is Nothing Then
            ' return firstToken
            Set parseCaseWhenStatement = firstToken

        ' 1.2. 否则，返回 `result`
        ' else {
        '     return result
        End If

        Exit Function
    End If

    ' 2. 执行: 匹配任意形式的列表达式--parseMixedColExpr，结果保存到 `tempElem`
    Set tempElem = parseMixedColExpr(tokenScanner)

    ' 3. 检查: `tempElem` 为空，则退出处理
    If tempElem Is Nothing Then
        ' 3.1. 检查: 如果 `result` 是空，则返回 `firstToken`
        If parseCaseWhenStatement Is Nothing Then
            Set parseCaseWhenStatement = firstToken

        ' 3.2. 否则，返回 `result`
        ' else {
        '     return result
        End If
        Exit Function
    End If

    ' 4. 检查: 如果 `result` 是空，则创建结果对象 `result`
    If parseCaseWhenStatement Is Nothing Then
        Set parseCaseWhenStatement = SqlFormatterHelper.createCaseCompositionElem(firstToken)
    End If

    ' 5. 否则，将 `tempElem` 保存到 `result`
    parseCaseWhenStatement.addComponent tempElem

    ' ==================================================
    ' 5. 匹配 then 和注释
    ' ==================================================
    ' 1. 遍历扫描器，如果扫描器用完了，则退出遍历
    While tokenScanner.hasNext
        ' 2. 从扫描器中获取一个 `token`，并保存 `tokenType = token.tokenType`
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' 3. 检查: 如果 tokenType 是注释
        If SqlHelper.isCommentToken(tokenType) Then
            ' 3.1. 将 `token` 保存到 `result`
            parseCaseWhenStatement.addComponent token
            ' 3.2. 扫描器向前推进
            tokenScanner.toNext

        ' 4. 否则，检查: 如果 tokenType 是 then
        ElseIf tokenType = SqlHelper.KEY_THEN Then
            ' 4.1. 将 `token` 保存到 `result`
            parseCaseWhenStatement.addComponent token
            ' 4.2. 扫描器向前推进
            tokenScanner.toNext
            ' 4.3. 设置 `hasThen = true`
            hasThen = True

        ' 5. 否则，退出处理
        Else
            GoTo consumeThenBreak
        End If
        ' 6. 从 2 开始下一轮遍历
    Wend
consumeThenBreak:

    ' ==================================================
    ' 6. 匹配一个 mixedColExpr
    ' ==================================================
    ' 1. 检查: 如果没有 then，则退出，并返回 `result`
    If hasThen = False Then
        ' return result
        Exit Function
    End If

    ' 2. 检查: 如果扫描器已经用完了，则退出处理，返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If

    ' 3. 执行: 匹配任意形式的列表达式--parseMixedColExpr，结果保存到 `tempElem`
    Set tempElem = parseMixedColExpr(tokenScanner)

    ' 4. 将 `tempElem` 保存到 `result`
    parseCaseWhenStatement.addComponent tempElem

    ' ==================================================
    ' 7. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 匹配case中的else表达式
'
' @param tokenScanner 扫描器
' @return else表达式的列对象
Public Function parseCaseElseStatement(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `SqlFormatterCaseCol result`
    '    这里直接使用 parseCaseElseStatement
    ' 2. token `SqlToken token`
    Dim token As SqlToken
    ' 3. 临时元素 `ISqlFormatterElem tempElem`
    Dim tempElem As ISqlFormatterElem
    ' 4. 第一个元素 `SqlToken firstToken`
    Dim firstToken As SqlToken

    ' ==================================================
    ' 2. 初始化 (处理 when 关键字)
    ' ==================================================
    ' 1. 从扫描器中弹出 `firstToken`
    Set firstToken = tokenScanner.getNext
    ' 2. 检查: 如果扫描器已经用完，则退出处理，并返回 `firstToken`
    If Not tokenScanner.hasNext Then
        ' return firstToken
        Set parseCaseElseStatement = firstToken
        Exit Function
    End If

    ' ==================================================
    ' 3. 吸收注释
    ' ==================================================
    ' 1. 遍历扫描器，如果扫描器用完了，则退出遍历
    While tokenScanner.hasNext
        ' 2. 从扫描器中获取一个 `token`
        Set token = tokenScanner.peek

        ' 3. 检查: 如果 `token` 的类型不是注释，则退出遍历: `SqlHelper.isCommentToken`
        If Not SqlHelper.isCommentToken(token.tokenType) Then
            GoTo whileBreak
        End If

        ' 4. 检查: 如果 `result` 是空，则创建结果对象 `result`: `SqlFormatterHelper.createCaseCompositionElem(firstToken)`
        If parseCaseElseStatement Is Nothing Then
            Set parseCaseElseStatement = SqlFormatterHelper.createCaseCompositionElem(firstToken)
        End If

        ' 5. 将 `token` 保存到 `result`
        parseCaseElseStatement.addComponent token

        ' 6. 扫描器向前推进
        tokenScanner.toNext

        ' 7. 从 2 开始下一轮遍历
    Wend
whileBreak:

    ' ==================================================
    ' 4. 匹配一个 mixedColExpr
    ' ==================================================
    ' 1. 检查: 如果扫描器没用完，则继续
    If tokenScanner.hasNext Then
        ' 2. 执行: 匹配任意形式的列表达式--parseMixedColExpr，结果保存到 `tempElem`
        Set tempElem = parseMixedColExpr(tokenScanner)
        ' 3. 检查: `tempElem` 不为空，则继续
        If Not tempElem Is Nothing Then
            ' 4. 检查: 如果 `result` 是空，则创建结果对象 `result`: `SqlFormatterHelper.createCaseCompositionElem(firstToken)`
            If parseCaseElseStatement Is Nothing Then
                Set parseCaseElseStatement = SqlFormatterHelper.createCaseCompositionElem(firstToken)
            End If

            ' 5. 将 `tempElem` 保存到 `result`
            parseCaseElseStatement.addComponent tempElem
        End If
    End if

    ' ==================================================
    ' 5. 退出处理，返回 `result`
    ' ==================================================
    ' 1. 检查: 如果 `result` 是空，则返回 `token`
    If parseCaseElseStatement Is Nothing Then
        ' return firstToken
        Set parseCaseElseStatement = firstToken
        Exit Function

    ' 2. 否则，返回 `result`
    ' else {
    '     return result
    End If
End Function

' 处理 cast 函数
'
' @param tokenScanner 扫描器 (ObjStack<SqlToken>)
' @param leftParenthesisStack 边界括号
' @return cast 函数的列对象
Public Function parseCastFunc( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 parseCastFunc
    ' 2. 函数体 `ISqlFormatterElem funcBody`
    Dim funcBody As ISqlFormatterElem
    ' 3. token `SqlToken token`
    Dim token As SqlToken

    ' ==================================================
    ' 2. 处理函数名 token
    ' ==================================================
    ' 1. 从扫描器中弹出一个 token，保存到 `result`
    Set parseCastFunc = tokenScanner.getNext
    ' 2. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If
    ' 3. 生成函数对象，结果保存到 `result`
    Set parseCastFunc = SqlFormatterHelper.createFuncCol(parseCastFunc)

    ' ==================================================
    ' 3. 处理注释
    ' ==================================================
    ' 1. 吸收右侧连续出现的注释到列对象，结果保存到 `result`
    Set parseCastFunc = consumeCommentToColRight(tokenScanner, parseCastFunc)

    ' ==================================================
    ' 4. 处理函数参数
    ' ==================================================
    ' 1. 检查: 如果扫描器没有用完，则继续
    If tokenScanner.hasNext Then
        ' 2. 从扫描器中获取一个 `token`
        Set token = tokenScanner.peek

        ' 3. 检查: 如果 token 类型是左括号，则继续: `SqlTokenType.LEFT_PARENTHESIS`
        If token.tokenType = SqlTokenType.LEFT_PARENTHESIS Then
            ' 4. 执行: 匹配cast函数的函数体，结果保存到 `funcBody`
            Set funcBody = parseCastFuncBody(tokenScanner)

            ' 5. 将 `funcBody` 保存到 `result`
            parseCastFunc.addComponent funcBody
        End If
    End If

    ' ==================================================
    ' 5. 括号配对
    ' ==================================================
    ' 1. 对 `result` 执行: 列对象括号配对，结果保存到 `result`
    Set parseCastFunc = matchColBoundaryParenthesis(parseCastFunc, tokenScanner, leftParenthesisStack)

    ' ==================================================
    ' 6. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 匹配cast函数的函数体
'
' @param tokenScanner 扫描器
' @return cast函数的函数体的 SqlFormatterContainerCol 列对象
Public Function parseCastFuncBody(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 parseCastFuncBody
    ' 2. token `SqlToken token`
    Dim token As SqlToken
    ' 3. 标记: 是否存在目标元素 `boolean hasTargetElem = false`
    '    这里用来标记: 函数体内部的逗号、右括号
    Dim hasTargetElem As Boolean
    ' 4. 函数参数元素 `ISqlFormatterElem paramElem`
    Dim paramElem As ISqlFormatterElem
    ' 5. tokenType `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType

    ' ==================================================
    ' 2. 处理左括号
    ' ==================================================
    ' 1. 从扫描器中弹出一个 token
    Set token = tokenScanner.getNext
    ' 2. 检查: 如果扫描器已经用完，则退出处理，返回 `token`
    If Not tokenScanner.hasNext Then
        Set parseCastFuncBody = token
    End If

    ' 3. 创建结果对象 `result`
    Set parseCastFuncBody = SqlFormatterHelper.createContainerCol
    ' 4. 吸收右侧连续出现的注释到列对象，结果保存到 `result`
    Set parseCastFuncBody = consumeCommentToColRight(tokenScanner, parseCastFuncBody)
    ' 5. 给 `result` 增加一个左边界括号
    parseCastFuncBody.addBoundaryLeftParenthesis
    ' 6. 检查: 如果扫描器已经用完，则退出处理，返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If

    ' ==================================================
    ' 3. 匹配 mixedColExpr
    ' ==================================================
    ' 1. 执行 [匹配任意形式的列表达式--parseMixedColExpr](#匹配任意形式的列表达式--parseMixedColExpr)，结果保存到 `paramElem`
    Set paramElem = parseMixedColExpr(tokenScanner)

    ' 2. 检查: 如果 `paramElem` 是空，则退出处理，返回 `result`
    If paramElem Is Nothing Then
        ' return result
        Exit Function
    End If

    ' 3. 将 `paramElem` 保存到 `result`
    parseCastFuncBody.addComponent paramElem

    ' ==================================================
    ' 4. 匹配 As
    ' ==================================================
    ' 1. 初始化标记 `hasTargetElem = false`
    hasTargetElem = False
    ' 2. 遍历扫描器，如果扫描器用完了，则退出遍历
    While tokenScanner.hasNext
        ' 3. 从扫描器中获取一个 `token`，并保存 `tokenType = token.tokenType`
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' 4. 检查，如果 tokenType 是 As
        If tokenType = SqlTokenType.KEY_AS Then
            ' 4.1. 将 token 保存到 `result`
            parseCastFuncBody.addComponent token
            ' 4.2. 向前推进扫描器
            tokenScanner.toNext
            ' 4.3. 设置标记 `hasTargetElem = true`
            hasTargetElem = True
            ' 4.4. 退出遍历
            GoTo consumeAsBreak

        ' 5. 否则，检查: 如果 tokenType 是注释
        ElseIf SqlHelper.isCommentToken(tokenType) Then
            ' 5.1. 将 token 保存到 `result`
            parseCastFuncBody.addComponent token
            ' 5.2. 向前推进扫描器
            tokenScanner.toNext

        ' 6. 否则，退出遍历
        Else
            GoTo consumeAsBreak
        End If
        ' 7. 从 3 开始下一轮遍历
    Wend
consumeAsBreak:

    ' ==================================================
    ' 5. 匹配 type
    ' ==================================================
    ' 1. 检查: 如果发现了 As，则继续: `hasTargetElem == true`
    If hasTargetElem Then
        ' 2. 遍历扫描器，如果扫描器用完了，则退出遍历
        While tokenScanner.hasNext
            ' 3. 从扫描器中获取一个 `token`，并保存 `tokenType = token.tokenType`
            Set token = tokenScanner.peek
            tokenType = token.tokenType

            ' 4. 检查，如果 tokenType 是
            If tokenType = SqlTokenType.STR_NAME Then
                ' 4.1. 将 token 保存到 `result`
                parseCastFuncBody.addComponent token
                ' 4.2. 向前推进扫描器
                tokenScanner.toNext
                ' 4.3. 退出遍历
                GoTo consumeTypeBreak

            ' 5. 否则，检查: 如果 tokenType 是注释
            ElseIf SqlHelper.isCommentToken(tokenType) Then
                ' 5.1. 将 token 保存到 `result`
                parseCastFuncBody.addComponent token
                ' 5.2. 向前推进扫描器
                tokenScanner.toNext

            ' 6. 否则，退出遍历
            Else
                GoTo consumeTypeBreak
            End If
            ' 6. 从 3 开始下一轮遍历
        Wend
consumeTypeBreak:
    End If

    ' ==================================================
    ' 6. 吸收一个右括号
    ' ==================================================
    ' 1. 遍历扫描器，如果扫描器用完了，则退出遍历
    While tokenScanner.hasNext
        ' 2. 从扫描器中获取一个 `token`，并保存 `tokenType = token.tokenType`
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' 4. 检查，如果 tokenType 是右括号
        If tokenType = SqlTokenType.RIGHT_PARENTHESIS Then
            ' 4.1. 给 `result` 增加一个右括号
            parseCastFuncBody.addBoundaryRightParenthesis
            ' 4.2. 向前推进扫描器
            tokenScanner.toNext
            ' 4.3. 退出遍历
            GoTo rightParenthesisBreak

        ' 5. 否则，检查: 如果 tokenType 是注释
        ElseIf SqlHelper.isCommentToken(tokenType) Then
            ' 5.1. 将 token 保存到 `result`
            parseCastFuncBody.addComponent token
            ' 5.2. 向前推进扫描器
            tokenScanner.toNext

        ' 6. 否则，退出遍历
        Else
            GoTo rightParenthesisBreak
        End If
        ' 7. 从 2 开始下一轮遍历
    Wend
rightParenthesisBreak:

    ' ==================================================
    ' 7. 退出处理，并返回 `result`
    ' ==================================================
    ' return result
End Function

' 匹配一个赋值语句
'
' @param tokenScanner 扫描器
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @return 赋值语句对象
Public Function parseAssignment( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem
    ' ==================================================
    ' 1. 快速结束
    ' ==================================================
    ' 1. 检查: 如果扫描器已经用完，则退出处理，并返回 `null`
    If Not tokenScanner.hasNext Then
        ' return null
        Exit Function
    End If

    ' ==================================================
    ' 2. 吸收注释 + 检查是否存在条件的第一个元素
    ' ==================================================
    ' 1. 声明变量
    ' 1.1. 偏移量 `int offset`
    Dim offset As Integer
    ' 1.2. 结果对象 (赋值语句) `SqlFormatterAssignment result`
    Dim result As SqlFormatterAssignment

    ' 2. 执行: 获取扫描器到下一个最近的不是sql各部分的起始关键字的偏移量，结果保存到 `offset`
    offset = SqlFormatterHelper.getNextNonSectionStartSqlKeyOffset(tokenScanner)
    ' 3. 检查: 如果 `offset == 0`，则退出处理，并返回 `null`
    If offset = 0 Then
        ' return null
        Exit Function
    End If
    ' 4. 创建结果对象 `result = new SqlFormatterAssignment()`
    Set result = New SqlFormatterAssignment
    ' 5. 检查: 如果 `offset > 1`，则执行: 吸收 offset - 1 个注释元素到 result 右侧
    If offset > 1 Then
        consumeComponentToColRight result, tokenScanner, offset - 1
    End If

    ' ==================================================
    ' 3. 匹配条件的第一个元素
    ' ==================================================
    ' 1. 临时元素 `ISqlFormatterElem tempElem`
    Dim tempElem As ISqlFormatterElem
    ' 2. 执行: 匹配任意形式的列表达式--parseMixedColExpr，结果保存到 `tempElem`
    Set tempElem = parseMixedColExpr(tokenScanner, True)

    ' 3. 检查: 如果 `tempElem` 是空，则退出处理，返回参数 `result`
    If tempElem Is Nothing Then
        ' return result
        Set parseAssignment = result
        Exit Function
    End If

    ' 4. 将 `tempElem` 保存到 `result`: `ISqlFormatterElem_addComponent`
    result.ISqlFormatterElem_addComponent tempElem

    ' 5. 检查: 如果扫描器已经用完
    If Not tokenScanner.hasNext Then
        ' return result
        Set parseAssignment = result
        Exit Function
    End If

    ' ==================================================
    ' 4. 处理赋值运算符
    ' ==================================================
    ' 1. 执行: 获取扫描器到下一个最近的赋值运算符的偏移量，结果保存到 `offset`
    offset = SqlFormatterHelper.getNextTargetElemOffset(tokenScanner, SqlTokenType.OPERA_EQ)
    ' 2. 检查: 如果 `offset == 0`
    If offset = 0 Then
        ' 2.1. 执行: 列对象括号配对，然后退出处理，并返回执行结果
        Set parseAssignment = matchColBoundaryParenthesis(result, tokenScanner, leftParenthesisStack)
        Exit Function

    ' 3. 否则，检查: 如果 `offset > 1`
    ElseIf offset > 1 Then
        ' 3.1 执行:吸收 offset - 1 个注释元素到 result 右侧，结果保存到 `result`
        consumeComponentToColRight result, tokenScanner, offset - 1
    End If

    ' 4. 从扫描器中弹出获取一个 `token`，并作为赋值运算符添加到 `result`: `setAssignmentOp`
    result.setAssignmentOp tokenScanner.getNext

    ' 5. 吸收一个同行注释到 result 右侧
    consumeOneShareLineCommentToColRight result, tokenScanner

    ' 6. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Set parseAssignment = result
        Exit Function
    End If

    ' ==================================================
    ' 5. 匹配条件的第二个元素
    ' ==================================================
    ' 1. 执行: 获取扫描器到下一个最近的不是sql各部分的起始关键字的偏移量，结果保存到 `offset`
    offset = SqlFormatterHelper.getNextNonSectionStartSqlKeyOffset(tokenScanner)

    ' 2. 检查: 如果 `offset == 0`，则退出处理，并返回 `result`
    If offset = 0 Then
        ' return result
        Set parseAssignment = result
        Exit Function

    ' 3. 否则，检查: 如果 `offset > 1`，则执行:吸收 offset - 1 个注释元素到 result 右侧，结果保存到 `result`
    ElseIf offset > 1 Then
        consumeComponentToColRight result, tokenScanner, offset - 1
    End If

    ' 4. 执行: 匹配任意形式的列表达式--parseMixedColExpr，结果保存到 `tempElem`
    Set tempElem = parseMixedColExpr(tokenScanner, True)
    ' 5. 检查: 如果 `tempElem` 是空，退出处理，并返回 `result`
    If tempElem Is Nothing Then
        ' return result
        Set parseAssignment = result
        Exit Function
    End If

    ' 6. 将 `tempElem` 保存到 `result`: `ISqlFormatterElem_addComponent`
    result.ISqlFormatterElem_addComponent tempElem

    ' ==================================================
    ' 6. 执行: 列对象括号配对，并返回执行结果
    ' ==================================================
    Set parseAssignment = matchColBoundaryParenthesis(result, tokenScanner, leftParenthesisStack)
End Function

' 匹配一个条件对象
'
' @param tokenScanner 扫描器
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @optional-param logicOp 起始的逻辑运算符
'                 默认值 = null
' @return 条件对象
Public Function parseCondition( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef leftParenthesisStack As ObjStack, _
    Optional ByRef logicOp As SqlToken = Nothing) As ISqlFormatterElem
    ' ==================================================
    ' 1. 快速结束
    ' ==================================================
    ' 1. 检查: 如果扫描器已经用完，则退出处理，并返回 `logicOp`
    If Not tokenScanner.hasNext Then
        ' return logicOp
        Set parseCondition = logicOp
        Exit Function
    End If

    ' ==================================================
    ' 2. 吸收注释 + 检查是否存在条件的第一个元素
    ' ==================================================
    ' 1. 声明变量
    ' 1.1. 偏移量 `int offset`
    Dim offset As Integer
    ' 1.2. 结果对象 (条件对象) `SqlFormatterTwoElemCondition result`
    Dim result As SqlFormatterTwoElemCondition

    ' 2. 执行: 获取扫描器到下一个最近的不是sql各部分的起始关键字的偏移量，结果保存到 `offset`
    offset = SqlFormatterHelper.getNextNonSectionStartSqlKeyOffset(tokenScanner)
    ' 3. 检查: 如果 `offset == 0`，则退出处理，并返回 `logicOp`
    If offset = 0 Then
        ' return logicOp
        Set parseCondition = logicOp
        Exit Function
    End If

    ' 4. 创建一个条件对象
    ' 4.1. 创建结果对象 `SqlFormatterTwoElemCondition result = new SqlFormatterTwoElemCondition()`
    Set result = New SqlFormatterTwoElemCondition
    ' 4.2. 将 `logicOp` 保存到 `result`: `ISqlFormatterCondition_setLogicOperator`
    result.ISqlFormatterCondition_setLogicOperator logicOp
    ' 5. 检查: 如果 `offset > 1`，则执行: 吸收 offset - 1 个注释元素到 result 右侧，结果保存到 `result`
    If offset > 1 Then
        Set result = consumeComponentToColRight(result, tokenScanner, offset - 1)
    End If

    ' ==================================================
    ' 3. 声明变量
    ' ==================================================
    ' 1. 临时元素 `ISqlFormatterElem tempElem`
    Dim tempElem As ISqlFormatterElem
    ' 2. token `SqlToken token`
    Dim token As SqlToken
    ' 3. tokenType `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType

    ' ==================================================
    ' 4. 匹配条件的第一个元素
    ' ==================================================
    ' 1. 执行: 匹配任意形式的列表达式--parseMixedColExpr，结果保存到 `tempElem`
    Set tempElem = parseMixedColExpr(tokenScanner, True)

    ' 2. 检查: 如果 `tempElem` 是空，则退出处理，返回参数 `result`
    If tempElem Is Nothing Then
        ' return result
        Set parseCondition = result
        Exit Function
    End If

    ' 3. 将 `tempElem` 保存到 `result`: `ISqlFormatterElem_addComponent`
    result.ISqlFormatterElem_addComponent tempElem

    ' 4. 检查: 如果扫描器已经用完
    If Not tokenScanner.hasNext Then
        ' return result
        Set parseCondition = result
        Exit Function
    End If

    ' ==================================================
    ' 5. 处理条件运算符
    ' ==================================================
    ' 1. 执行: 获取扫描器到下一个最近的条件运算符的偏移量，结果保存到 `offset`
    offset = SqlFormatterHelper.getNextConditionOperatorOffset(tokenScanner)
    ' 2. 检查: 如果 `offset == 0`
    If offset = 0 Then
        ' 2.1. 执行: 列对象括号配对，然后退出处理，并返回执行结果
        Set parseCondition = matchColBoundaryParenthesis(result, tokenScanner, leftParenthesisStack)
        Exit Function

    ' 3. 否则，检查: 如果 `offset > 1`
    ElseIf offset > 1 Then
        ' 3.1 执行:吸收 offset - 1 个注释元素到 result 右侧，结果保存到 `result`
        Set result = consumeComponentToColRight(result, tokenScanner, offset - 1)
    End If

    ' 4. 从扫描器中获取一个 `token`，并保存类型到 `tokenType`
    Set token = tokenScanner.peek
    tokenType = token.tokenType

    ' 5. 检查: 如果 token 类型是 Not: `SqlTokenType.KEY_NOT`
    If tokenType = SqlTokenType.KEY_NOT Then
        ' 5.1. [获取Not开头的条件运算符](#获取Not开头的条件运算符)，结果保存到 `tempElem`
        Set tempElem = getNotConditionOp(tokenScanner)
        ' 5.2. 检查: 如果 `tempElem` 是空，则退出处理，并返回 `result`
        If tempElem Is Nothing Then
            ' return result
            Set parseCondition = result
            Exit Function
        End If
        ' 5.3. 将 `tempElem` 作为条件运算符添加到 `result`: `ISqlFormatterCondition_setConditionOperator`
        result.ISqlFormatterCondition_setConditionOperator tempElem

        ' 5.4. 保存条件运算符的类型
        tokenType = tempElem.getType

    ' 6. 否则
    Else
        ' 6.1. 向前推进扫描器
        tokenScanner.toNext
        ' 6.2. 将 `token` 作为条件运算符添加到 `result`: `ISqlFormatterCondition_setConditionOperator`
        result.ISqlFormatterCondition_setConditionOperator token
    End If

    ' 7. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Set parseCondition = result
        Exit Function
    End If

    ' ==================================================
    ' 6. 生成结果对象
    ' ==================================================
    ' 1. 从条件运算符开始匹配一个二元条件对象--parseConditionFromOperator，并返回执行结果
    '    需要携带参数中的左边界括号
    Set parseCondition = parseConditionFromOperator(tokenScanner, leftParenthesisStack, result, tokenType)
End Function

' 从条件运算符开始匹配一个二元条件对象
'
' @param tokenScanner 扫描器
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @param condition 条件对象
' @param conditionOpType 条件运算符的类型
' @return 条件对象
Public Function parseConditionFromOperator( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef leftParenthesisStack As ObjStack, _
    ByRef condition As SqlFormatterTwoElemCondition, _
    ByRef conditionOpType As SqlTokenType) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `SqlFormatterTwoElemCondition result`
    Dim result As SqlFormatterTwoElemCondition
    ' 2. 临时元素 `ISqlFormatterElem tempElem`
    Dim tempElem As ISqlFormatterElem
    ' 3. 偏移量 `int offset`
    Dim offset As Integer

    ' ==================================================
    ' 2. 吸收注释
    ' ==================================================
    ' 1. 执行: 获取扫描器到下一个最近的不是sql各部分的起始关键字的偏移量，结果保存到 `offset`
    offset = SqlFormatterHelper.getNextNonSectionStartSqlKeyOffset(tokenScanner)
    ' 2. 检查: 如果 `offset == 0`，则退出处理，并返回 `condition`
    If offset = 0 Then
        ' return condition
        Set parseConditionFromOperator = condition
        Exit Function

    ' 3. 否则，如果 `offset > 1`，执行:吸收 offset - 1 个注释元素到 condition 右侧，结果保存到 `condition`
    ElseIf offset > 1 Then
        Set condition = consumeComponentToColRight(condition, tokenScanner, offset -1)
    End If

    ' ==================================================
    ' 3. 按照运算符的类型来解析条件的第二个元素
    ' ==================================================
    ' 1. 处理 BETWEEN
    ' 1.1. 检查: 如果条件运算符的类型是 BETWEEN，则继续
    If conditionOpType = SqlTokenType.KEY_BETWEEN Then
        ' 1.2. 执行: 解析Between中的参数--parseBetweenOpParam，结果保存到 `tempElem`
        Set tempElem = parseBetweenOpParam(tokenScanner)

    ' 2. 否则，处理 IN
    ' 2.1. 检查: 如果条件运算符的类型是 IN，则继续
    ElseIf conditionOpType = SqlTokenType.KEY_IN Then
        ' 2.2. 执行: 解析in运算符的参数--parseInOpParam，结果保存到 `tempElem`
        Set tempElem = parseInOpParam(tokenScanner)

    ' 3. 否则，处理其他的运算符
    Else
        ' 3.1. 匹配任意形式的列表达式--parseMixedColExpr，保存到 `tempElem`
        Set tempElem = parseMixedColExpr(tokenScanner, true)
    End If

    ' 4. 将 `tempElem` 保存到 `condition`
    condition.ISqlFormatterElem_addComponent tempElem

    ' ==================================================
    ' 4. 执行: 列对象括号配对，并返回执行结果
    ' ==================================================
    Set parseConditionFromOperator = matchColBoundaryParenthesis(condition, tokenScanner, leftParenthesisStack)
End Function

' 解析 Between 中的参数
'
' @param tokenScanner 扫描器
' @return 包含 Between 中参数的对象 SqlFormatterBetweenRange
Public Function parseBetweenOpParam(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `SqlFormatterBetweenRange result`
    Dim result As SqlFormatterBetweenRange
    ' 2. 临时元素 `ISqlFormatterElem tempElem`
    Dim tempElem As ISqlFormatterElem
    ' 3. token `SqlToken token`
    Dim token As SqlToken

    ' ==================================================
    ' 2. 标记扫描器: `mark`
    ' ==================================================
    tokenScanner.mark

    ' ==================================================
    ' 3. 处理第一个元素
    ' ==================================================
    ' 1. 执行: 匹配任意形式的列表达式--parseMixedColExpr，结果保存到 `tempElem`
    Set tempElem = parseMixedColExpr(tokenScanner, True)

    ' 2. 检查: 如果 `tempElem` 是空
    If tempElem Is Nothing Then
        ' 2.1. 重置扫描器位置并清除标记: `resetAndClearMark`
        tokenScanner.resetAndClearMark
        ' 2.2. 退出处理，并返回 null
        ' return null
        Exit Function
    End If

    ' 3. 检查: 如果扫描器已经用完了
    If Not tokenScanner.hasNext Then
        ' 3.1. 清除扫描器的标记: `clearMark`
        tokenScanner.clearMark
        ' 3.2. 退出处理，并返回 `tempElem`
        ' return tempElem
        Set parseBetweenOpParam = tempElem
        Exit Function
    End If

    ' ==================================================
    ' 4. 匹配 and
    ' ==================================================
    ' 1. 从扫描器中获取一个 `token`
    Set token = tokenScanner.peek
    ' 2. 检查: 如果 token 的类型不是 and
    If token.tokenType <> SqlTokenType.KEY_AND Then
        ' 2.1. 重置扫描器位置并清除标记: `resetAndClearMark`
        tokenScanner.resetAndClearMark
        ' 2.2. 退出处理，并返回 null
        ' return null
        Exit Function
    End If
    ' 3. 否则，扫描器向前推进
    tokenScanner.toNext

    ' ==================================================
    ' 5. 创建结果对象
    ' ==================================================
    ' 1. 创建结果对象
    Set result = New SqlFormatterBetweenRange
    ' 2. 将第一个元素 `tempElem` 保存到 `result`
    result.ISqlFormatterElem_addComponent tempElem
    ' 3. 将 `token`(And 运算符) 保存到 `result`
    result.ISqlFormatterElem_addComponent token
    ' 4. 清除扫描器的标记: `clearMark`
    tokenScanner.clearMark
    ' 5. 检查: 如果扫描器已经用完，则退出处理，返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Set parseBetweenOpParam = result
        Exit Function
    End If

    ' ==================================================
    ' 6. 匹配第二个元素
    ' ==================================================
    ' 1. 执行: 匹配任意形式的列表达式--parseMixedColExpr，结果保存到 `tempElem`
    Set tempElem = parseMixedColExpr(tokenScanner, True)

    ' 2. 将第二个元素 `tempElem` 保存到 `result`
    result.ISqlFormatterElem_addComponent tempElem

    ' ==================================================
    ' 7. 退出处理，返回 `result`
    ' ==================================================
    Set parseBetweenOpParam = result
End Function

' 解析 in 运算符的参数
'
' @param tokenScanner 扫描器
' @return 包含 in 运算符参数的对象
Public Function parseInOpParam(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 下一个左括号的偏移量 `int nextLeftParenthesisOffset`
    Dim nextLeftParenthesisOffset As Integer
    ' 2. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 parseInOpParam
    ' 3. 遍历索引 `int i`
    Dim i As Integer
    ' 4. 结果对象 `ISqlFormatterElem tempElem`
    Dim tempElem As ISqlFormatterElem

    ' ==================================================
    ' 2. 确认是否存在左括号
    ' ==================================================
    ' 1. 获取扫描器到下一个最近的左括号的偏移量，结果保存到 `nextLeftParenthesisOffset`
    nextLeftParenthesisOffset = SqlFormatterHelper.getNextLeftParenthesisOffset(tokenScanner)
    ' 2. 检查: 如果没有左括号，则退出处理，并返回 null
    If nextLeftParenthesisOffset = 0 Then
        ' return null
        Exit Function
    End If

    ' ==================================================
    ' 3. 如果下一个 token 就是括号
    ' ==================================================
    ' 1. 检查: 如果左括号偏移量是 1，则继续
    If nextLeftParenthesisOffset = 1 Then
        ' 2. 执行: 匹配括号包裹的多个列对象--parseParenthesisWrapperMultiCol，结果保存到 `result`
        Set parseInOpParam = parseParenthesisWrapperMultiCol(tokenScanner)
        ' 3. 退出处理，返回 `result`
        ' return result
        Exit Function
    End If

    ' ==================================================
    ' 4. 处理开头包含注释的情况
    ' ==================================================
    ' 1. 标记扫描器: `mark`
    tokenScanner.mark
    ' 2. 吸收开头的注释
    ' 2.1. 生成一个容器列，保存到 `result`
    Set parseInOpParam = SqlFormatterHelper.createContainerCol
    ' 2.2. 从 `i = 1` 开始到 `nextLeftParenthesisOffset - 1` 遍历
    For i = 1 To nextLeftParenthesisOffset - 1
        ' 2.3. 从扫描器中弹出一个元素，并添加到 `result`
        parseInOpParam.addComponent tokenScanner.getNext
    Next
    ' 3. 执行: 匹配括号包裹的多个列对象--parseParenthesisWrapperMultiCol，结果保存到 `tempElem`
    Set tempElem = parseParenthesisWrapperMultiCol(tokenScanner)

    ' 4. 检查: 如果 `tempElem` 是空
    If tempElem Is Nothing Then
        ' 4.1. 重置扫描器位置并清除标记: `resetAndClearMark`
        tokenScanner.resetAndClearMark
        ' 4.2. 退出处理，并返回 null
        ' return null
        Set parseInOpParam = Nothing
        Exit Function
    End If
    ' 5. 将 `tempElem` 保存到 `result`
    parseInOpParam.addComponent tempElem
    ' 6. 清除扫描器的标记: `clearMark`
    tokenScanner.clearMark
    ' 7. 退出处理，返回 `result`
    ' return result
End Function

' 匹配 limit
'
' @param tokenScanner 扫描器
' @return SqlFormatterKeySectionEnv
Public Function parseLimitEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. key `SqlToken key`
    Dim key As SqlToken
    ' 2. 第一个token `SqlToken firstToken`
    Dim firstToken As SqlToken
    ' 3. token `SqlToken token`
    Dim token As SqlToken
    ' 4. tokenType `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType
    ' 5. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 parseLimitEnv

    ' ==================================================
    ' 2. 处理 LIMIT
    ' ==================================================
    ' 1. 从扫描器中弹出一个 `key`
    Set key = tokenScanner.getNext
    ' 2. 检查: 如果扫描器已经用完，则退出处理，并返回 `key`
    If Not tokenScanner.hasNext Then
        ' return key
        Set parseLimitEnv = key
        Exit Function
    End If

    ' ==================================================
    ' 3. 匹配一个数字(这里无法确定是哪一种: `offset, row_count`, `row_count`, `row_count OFFSET offset`)
    ' ==================================================
    ' 1. 从扫描器中获取一个 token，保存到 `firstToken`
    Set firstToken = tokenScanner.peek
    ' 2. 检查: 如果 `firstToken` 类型不是数字
    If firstToken.tokenType <> SqlTokenType.STR_NUM Then
        ' 2.1. 退出处理，并返回 `key`
        ' return key
        Set parseLimitEnv = key
        Exit Function
    End If
    ' 3. 向前推进扫描器
    tokenScanner.toNext

    ' 4. 检查: 如果扫描器已经用完
    If Not tokenScanner.hasNext Then
        ' 4.1. 初始化 `result`: `SqlFormatterHelper.createContainerCol`
        Set parseLimitEnv = SqlFormatterHelper.createContainerCol
        ' 4.2. 将 `key` 保存到 `result`
        parseLimitEnv.addComponent key
        ' 4.3. 将 `firstToken` 保存到 `result`
        parseLimitEnv.addComponent firstToken
        ' 4.4. 退出处理，返回 `result`
        ' return result
        Exit Function
    End If

    ' ==================================================
    ' 5. 匹配 `offset, row_count` 或 `row_count OFFSET offset` (下一个是逗号或 offset, 再下一个是数值)
    ' ==================================================
    ' 1. 从扫描器中获取一个 `token`，并保存类型到 `tokenType`
    Set token = tokenScanner.peek
    tokenType = token.tokenType

    ' 2. 检查: 如果 token 类型是逗号: `SqlTokenType.COMMA` 或者 `SqlTokenType.KEY_OFFSET`，则继续
    If tokenType = SqlTokenType.COMMA Or tokenType = SqlTokenType.KEY_OFFSET Then
        ' 3. 初始结果对象
        ' 3.1. 初始化 `result`: `SqlFormatterHelper.createContainerCol`
        Set parseLimitEnv = SqlFormatterHelper.createContainerCol
        ' 3.2. 将 `key` 保存到 `result`
        parseLimitEnv.addComponent key
        ' 3.3. 将 `firstToken` 保存到 `result`
        parseLimitEnv.addComponent firstToken
        ' 3.4. 将 `token` 保存到 `result`
        parseLimitEnv.addComponent token

        ' 4. 向前推进扫描器
        tokenScanner.toNext

        ' 5. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
        If Not tokenScanner.hasNext Then
            ' return result
            Exit Function
        End If

        ' 6. 从扫描器中获取一个 `token`
        Set token = tokenScanner.peek

        ' 7. 检查: 如果 token 类型是数字
        If token.tokenType = SqlTokenType.STR_NUM Then
            ' 7.1. 将 `token` 保存到 `result`
            parseLimitEnv.addComponent token
            ' 7.2. 向前推进扫描器
            tokenScanner.toNext
        End If

    ' ==================================================
    ' 6. 退化为 `LIMIT row_count` 的情况
    ' ==================================================
    ' 1. (接 5) 如果无法匹配 `offset, row_count` 或 `row_count OFFSET offset`，则继续
    Else
        ' 2. 初始化 `result`: `SqlFormatterHelper.createContainerCol`
        Set parseLimitEnv = SqlFormatterHelper.createContainerCol
        ' 3. 将 `key` 保存到 `result`
        parseLimitEnv.addComponent key
        ' 4. 将 `firstToken` 保存到 `result`
        parseLimitEnv.addComponent firstToken
    End If

    ' ==================================================
    ' 7. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 解析条件作用域
'
' @param tokenScanner 扫描器
' @return 条件作用域 SqlFormatterKeySectionEnv
Public Function parseConditionEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. token `SqlToken token`
    Dim token As SqlToken
    ' 2. 结果对象 `ISqlFormatterElem result`
    '   这里直接使用 parseConditionEnv
    ' 3. 临时条件对象 `ISqlFormatterElem condition`
    Dim condition As ISqlFormatterElem
    ' 4. 多条件组合对象 `SqlFormatterMultiConditions multiConditions`
    Dim multiConditions As SqlFormatterMultiConditions
    ' 5. 内部左括号栈 `ObjStack<SqlToken> innerLeftParenthesisStack`
    Dim innerLeftParenthesisStack As ObjStack

    ' ==================================================
    ' 2. 处理第一个 token
    ' ==================================================
    ' 1. 从扫描器中弹出一个 token，保存到 `result`
    Set parseConditionEnv = tokenScanner.getNext
    ' 2. 为 result 吸收一个同行注释并合并为TokenGroup，结果保存到 `result`
    Set parseConditionEnv = consumeShareLineCommentToTokenGroup(parseConditionEnv, tokenScanner)
    ' 3. 检查: 如果扫描器已经用完，则退出处理，返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If

    ' ==================================================
    ' 3. 解析第一个条件对象
    ' ==================================================
    ' 1. 执行: 匹配一个条件对象--parseCondition，结果保存到 `condition`
    Set condition = parseCondition(tokenScanner, Nothing, Nothing)
    ' 2. 检查: 如果 `condition` 是空，则退出处理，并返回 `result`
    If condition Is Nothing Then
        ' return result
        Exit Function
    End If

    ' 3. 初始化作用域结果对象 `result`: `SqlFormatterHelper.createKeyStartEnv`
    Set parseConditionEnv = SqlFormatterHelper.createKeyStartEnv(parseConditionEnv, parseConditionEnv.getType)

    ' 4. 创建多条件组合对象 `multiConditions = new SqlFormatterMultiConditions()`
    Set multiConditions = New SqlFormatterMultiConditions
    ' 5. 将 `condition` 保存到 `multiConditions`
    multiConditions.addCondition condition
    ' 6. 将 `multiConditions` 保存到 `result`
    parseConditionEnv.addComponent multiConditions

    ' ==================================================
    ' 4. 遍历解析后面的条件
    ' ==================================================
    ' 1. 遍历扫描器，如果扫描器以用完，则退出遍历
    While tokenScanner.hasNext
        ' 2. 解析左括号，并保存到 `innerLeftParenthesisStack`
        Set innerLeftParenthesisStack = parseStartLeftParenthesis(tokenScanner)

        ' 3. 检查: 如果 `innerLeftParenthesisStack != null`
        If Not innerLeftParenthesisStack Is Nothing Then
            ' 3.1. 检查: 如果扫描器用完
            If Not tokenScanner.hasNext Then
                ' 3.1.1. 导出左括号栈到作用域 `multiConditions`
                appendStackToEnv multiConditions, innerLeftParenthesisStack
                ' 2. 将 `innerLeftParenthesisStack` 回收到池中
                UtilsPool.releaseObjStack innerLeftParenthesisStack
                ' 3.1.3. 重置 `innerLeftParenthesisStack = null`
                Set innerLeftParenthesisStack = Nothing
                ' 3.1.4. 退出遍历
                GoTo whileBreak
            End If
        End If

        ' 4. 检查: 逻辑运算符
        ' 4.1. 从扫描器中获取一个 `token`
        Set token = tokenScanner.peek
        ' 4.2. 检查: 如果 token 不是逻辑运算符
        If Not SqlHelper.tokenTypeIsLogicOperator(token.tokenType) Then
            ' 4.2.1. 将左括号栈回退到扫描器内
            putBackStackToScanner tokenScanner, innerLeftParenthesisStack
            ' 4.2.2. 将 `innerLeftParenthesisStack` 回收到池中
            UtilsPool.releaseObjStack innerLeftParenthesisStack
            ' 4.2.3. 退出遍历
            GoTo whileBreak
        End If

        ' 4.3. 扫描器向前推进
        tokenScanner.toNext

        ' 5. 执行: [匹配一个条件对象--parseCondition](#匹配一个条件对象--parseCondition)，结果保存到 `condition`
        Set condition = parseCondition(tokenScanner, innerLeftParenthesisStack, token)

        ' 6. 导出(可能多余的)左括号栈到作用域 `multiConditions`
        appendStackToEnv multiConditions, innerLeftParenthesisStack

        ' 7. 将 `condition` 保存到 `multiConditions`
        multiConditions.addCondition condition
        ' 8. 将 `innerLeftParenthesisStack` 回收到池中
        UtilsPool.releaseObjStack innerLeftParenthesisStack
    Wend
whileBreak:

    ' ==================================================
    ' 6. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 匹配任意形式的列表达式
'
' @param tokenScanner 扫描器
' @optional-param needStopInFirstNonArithmetic 遇到第一个非算数运算符时，是否需要停止解析
'                 默认值 = false
' @return 列对象
Public Function parseMixedColExpr( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    Optional ByVal needStopInFirstNonArithmetic As Boolean = False) As ISqlFormatterElem

    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果列对象 `ISqlFormatterElem result`
    '    这里直接使用 parseMixedColExpr
    ' 2. 扫描器的活动索引: `long tokenScannerActiveIndex`
    Dim tokenScannerActiveIndex As Long

    ' ==================================================
    ' 2. 匹配一个列
    ' ==================================================
    ' 1. 执行: [匹配一个单独的列对象---parseSingleCol](#匹配一个单独的列对象---parseSingleCol)，结果保存到 `result`
    Set parseMixedColExpr = parseSingleCol(tokenScanner, needStopInFirstNonArithmetic)

    ' 2. 检查: 如果 `result` 是 null
    If parseMixedColExpr Is Nothing Then
        ' 2.1. 退出处理，返回 null
        ' return null
        Exit Function
    End If

    ' ==================================================
    ' 3. 尝试继续匹配从运算符开始的表达式
    ' ==================================================
    ' 1. 遍历扫描器，直到扫描器用完
    While tokenScanner.hasNext
        ' 2. 记录当前扫描器的活动索引: `tokenScannerActiveIndex = tokenScanner.ActiveIndex`
        tokenScannerActiveIndex = tokenScanner.ActiveIndex
        ' 3. 从运算符开始匹配表达式--parseExprColStartWithOperator，结果保存到 `result`
        Set parseMixedColExpr = parseExprColStartWithOperator( _
            parseMixedColExpr, _
            Nothing, _
            tokenScanner, _
            needStopInFirstNonArithmetic)

        ' 4. 检查: 如果活动索引没有移动，则退出遍历
        If tokenScannerActiveIndex = tokenScanner.ActiveIndex Then
            GoTo whileBreak
        End If
    Wend
whileBreak:

    ' ==================================================
    ' 4. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 匹配Exists表达式
'
' @param workCol 保存结果的列对象 (可能为空)
' @param tokenScanner` 扫描器 (ObjStack<SqlToken>)
' @param leftParenthesisStack` 左括号栈 (可能为空)
' @return Exists表达式的列对象
Public Function parseExistsExpr( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem

    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 parseExistsExpr
    ' 2. 临时列对象 `ISqlFormatterElem tempElem`
    Dim tempElem As ISqlFormatterElem
    ' 3. token `SqlToken token`
    Dim token As SqlToken

    ' ==================================================
    ' 2. 处理 `Exists` 关键字
    ' ==================================================
    ' 1. 从扫描器中弹出一个 `token`，并保存到 `result`
    Set token = tokenScanner.getNext
    ' 2. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return token
        Set parseExistsExpr = token
        Exit Function
    End If

    ' ==================================================
    ' 3. 处理 `Exists` 后的子查询
    ' ==================================================
    ' 1. 匹配一个子查询，结果保存到 `tempElem`
    Set tempElem = parseSqlEnv(tokenScanner, True)

    ' 2. 检查: 如果 `tempElem` 是空，则退出处理，并返回 `token`
    If tempElem Is Nothing Then
        ' return token
        Set parseExistsExpr = token
        Exit Function
    End If

    ' 3. 创建结果对象
    ' 3.1. 创建一个条件对象保存到 `result`: `SqlFormatterHelper.createCondition`
    Set parseExistsExpr = SqlFormatterHelper.createCondition
    ' 3.2. 将 token 保存到 `result`
    parseExistsExpr.addComponent token
    ' 3.3. 将 `tempElem` 保存到 `result`
    parseExistsExpr.addComponent tempElem

    ' ==================================================
    ' 4. 执行列对象括号配对，结果保存到 `result`
    ' ==================================================
    Set parseExistsExpr = matchColBoundaryParenthesis(parseExistsExpr, tokenScanner, leftParenthesisStack)

    ' ==================================================
    ' 5. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 匹配一个单独的列对象---驱动部分
'
' @param tokenScanner 扫描器
' @optional-param needStopInFirstNonArithmetic 遇到第一个非算数运算符时，是否需要停止解析
'                 默认值 = false
' @return 列对象
Public Function parseSingleCol( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    Optional ByVal needStopInFirstNonArithmetic As Boolean = False) As ISqlFormatterElem

    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果列对象 `ISqlFormatterElem result`
    Dim result As ISqlFormatterElem
    ' 2. 左括号栈: `ObjStack<SqlToken> leftParenthesisStack = null`
    Dim leftParenthesisStack As ObjStack

    ' ==================================================
    ' 2. 处理左括号
    ' ==================================================
    ' 1. 尝试解析左括号，并保存到 `leftParenthesisStack`
    Set leftParenthesisStack = parseStartLeftParenthesis(tokenScanner)

    ' 2. 检查: 如果 `leftParenthesisStack != null`
    If Not leftParenthesisStack Is Nothing Then
        ' 2.1. 检查: 如果扫描器用完
        If Not tokenScanner.hasNext Then
            ' 2.1.1. 导出左括号栈到作用域
            Set parseSingleCol = clearStackToContainerCol(leftParenthesisStack)
            ' 2.1.2. 将左括号栈释放回池中
            UtilsPool.releaseObjStack leftParenthesisStack
            ' 2.1.3. 退出处理，返回 `result`
            ' return result
            Exit Function
        End If
    End If

    ' ==================================================
    ' 3. 执行解析
    ' ==================================================
    ' 1. 执行: 匹配一个单独的列对象---doParseSingleCol--执行解析的部分，结果保存到 `result`
    Set parseSingleCol = doParseSingleCol( _
        tokenScanner, _
        leftParenthesisStack, _
        needStopInFirstNonArithmetic)

    ' 2. 检查: 如果 `result` 是 null
    If parseSingleCol Is Nothing Then
        ' 2.1. 检查: 如果左括号栈存在
        If Not leftParenthesisStack Is Nothing Then
            ' 2.1.1. 将左括号栈回退到扫描器内
            putBackStackToScanner tokenScanner, leftParenthesisStack
            ' 2.1.2. 将左括号栈释放回池中
            UtilsPool.releaseObjStack leftParenthesisStack
        End If

        ' 2.2. 退出处理，返回 null
        ' return null
        Exit Function
    End If

    ' ==================================================
    ' 4. 处理剩余的左括号
    ' ==================================================
    ' 1. 检查: 如果左括号栈没有用完，则继续: `UtilsStack.isEmptyObjStack(leftParenthesisStack) == false`
    If UtilsStack.isEmptyObjStack(leftParenthesisStack) = False Then
        ' 1.1 吸收多余的左括号栈到列对象左侧，结果保存到 `result`
        Set parseSingleCol = consumeLeftParenthesisStackAllTokenToCol(parseSingleCol, leftParenthesisStack)
    End If
    ' 2. 将左括号栈释放回池中
    UtilsPool.releaseObjStack leftParenthesisStack

    ' ==================================================
    ' 5. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 匹配一个单独的列对象---执行解析的部分
'
' @param tokenScanner 扫描器
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @optional-param needStopInFirstNonArithmetic 遇到第一个非算数运算符时，是否需要停止解析
'                 默认值 = false
' @return 列对象
Private Function doParseSingleCol( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef leftParenthesisStack As ObjStack, _
    Optional ByVal needStopInFirstNonArithmetic As Boolean = False) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果列对象 `ISqlFormatterElem result`
    '    这里直接使用 doParseSingleCol
    ' 2. 临时列对象 `ISqlFormatterElem tempElem`
    Dim tempElem As ISqlFormatterElem
    ' 3. token `SqlToken token`
    Dim token As SqlToken
    ' 4. token 类型 `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType
    ' 5. 扫描器的活动索引: `long tokenScannerActiveIndex`
    Dim tokenScannerActiveIndex As Long

    ' ==================================================
    ' 2. 解析开头的 `NOT | BINARY`
    ' ==================================================
    ' ==============================
    ' 2.1. 遍历处理这两种元素
    ' ==============================
    ' 1. 遍历扫描器，直到扫描器用完
    While tokenScanner.hasNext
        ' 2. 从扫描器中获取一个 `token`，并保存它的类型到 `tokenType`
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' 3. 检查: 如果 tokenType 是 `SqlTokenType.KEY_NOT` 或者 `SqlTokenType.KEY_BINARY`
        If tokenType = SqlTokenType.KEY_NOT Or tokenType = SqlTokenType.KEY_BINARY Then
            ' 3.1. 检查: 如果 `result` 是空，则创建一个包装列对象保存到 `result`
            If doParseSingleCol Is Nothing Then
                Set doParseSingleCol = SqlFormatterHelper.createContainerCol
            End If

            ' 3.2. 将 `token` 保存到 `result`
            doParseSingleCol.addComponent token

            ' 3.3. 扫描器向前推进
            tokenScanner.toNext

        ' 4. 否则，退出遍历
        Else
            GoTo consumeNBBreak
        End If
        ' 5. 从 1 开始，进入下一轮遍历
    Wend
consumeNBBreak:

    ' ==============================
    ' 2.2. 检查: 如果扫描器已经用完
    ' ==============================
    If Not tokenScanner.hasNext Then
        ' 1. 退出，并返回 `result`
        ' return result
        Exit Function
    End If

    ' ==================================================
    ' 3. 处理开头存在 `NOT | BINARY` 的情况
    ' ==================================================
    ' 1. 检查: 如果 `result` 已经被创建了，即开头存在 `NOT | BINARY`，则继续
    If Not doParseSingleCol Is Nothing Then
        ' 2. 调用: 匹配一个单独的列对象---parseSingleCol，解析结果保存到 `tempElem`
        Set tempElem = parseSingleCol(tokenScanner, needStopInFirstNonArithmetic)
        ' 3. 将 `tempElem` 保存到 `result`
        doParseSingleCol.addComponent tempElem

    ' ==================================================
    ' 4. 处理开头不存在 `NOT | BINARY` 的情况 (匹配一个单独的列对象)
    ' ==================================================
    ' 1. (接 4) 否则 `result` 未创建，则继续
    Else
        ' 2. 从扫描器中获取一个 `token`，并保存它的类型到 `tokenType`
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' 3. 检查: 如果 `tokenType` 是 `SqlTokenType.KEY_EXISTS`
        If tokenType = SqlTokenType.KEY_EXISTS Then
            ' 3.1. 执行: 匹配Exists表达式--parseExistsExpr，结果保存到 `result`
            Set doParseSingleCol = parseExistsExpr(tokenScanner, leftParenthesisStack)

        ' 4. 否则
        Else
            ' 4.1. 匹配单列名称表达式--parseColExpr，结果保存到 `result`
            Set doParseSingleCol = parseColExpr(tokenScanner, leftParenthesisStack)
        End If

        ' 5. 检查: 如果 `result` 是 null
        If doParseSingleCol Is Nothing Then
            ' 5.1. 退出处理，返回 null
            ' return null
            Exit Function
        End If
    End If

    ' ==================================================
    ' 5. 在括号用完前，继续吸收表达式
    ' ==================================================
    ' 1. 检查: 如果左括号栈没用完，则继续: `UtilsStack.isEmptyObjStack(leftParenthesisStack) == false`
    If UtilsStack.isEmptyObjStack(leftParenthesisStack) = False Then
        ' 1. 遍历扫描器，直到扫描器用完或者左括号栈为空
        While tokenScanner.hasNext And leftParenthesisStack.Count > 0
            ' 2. 记录当前扫描器的活动索引: `tokenScannerActiveIndex = tokenScanner.ActiveIndex`
            tokenScannerActiveIndex = tokenScanner.ActiveIndex
            ' 3. 从运算符开始匹配表达式--parseExprColStartWithOperator，结果保存到 `result`
            Set doParseSingleCol = parseExprColStartWithOperator( _
                doParseSingleCol, _
                leftParenthesisStack, _
                tokenScanner, _
                needStopInFirstNonArithmetic)

            ' 4. 检查: 如果活动索引没有移动，则退出遍历: `tokenScannerActiveIndex == tokenScanner.ActiveIndex`
            If tokenScannerActiveIndex = tokenScanner.ActiveIndex Then
                GoTo whileBreak
            End If
        Wend
whileBreak:
    End If

    ' ==================================================
    ' 6. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 从运算符开始匹配表达式
'
' @param startElem 起始元素
' @param leftParenthesisStack 左边界括号栈 (ObjStack<SqlToken>)
' @param tokenScanner 扫描器
' @optional-param needStopInFirstNonArithmetic 遇到第一个非算数运算符时，是否需要停止解析
'                 默认值 = false
Public Function parseExprColStartWithOperator( _
    ByRef startElem As ISqlFormatterElem, _
    ByRef leftParenthesisStack As ObjStack, _
    ByRef tokenScanner As ObjArrayRefScanner, _
    Optional ByRef needStopInFirstNonArithmetic As Boolean = False) As ISqlFormatterElem

    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果列对象 `ISqlFormatterElem result`
    '    这里直接使用 parseExprColStartWithOperator

    ' 2. 临时列对象 `ISqlFormatterElem tempElem`
    Dim tempElem As ISqlFormatterElem
    ' 3. token `SqlToken token`
    Dim token As SqlToken
    ' 4. token 类型 `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType
    ' 5. 偏移量 `int offset`
    Dim offset As Integer

    ' ==================================================
    ' 2. 初始化
    ' ==================================================
    ' 1. 设置 `result = startElem`
    Set parseExprColStartWithOperator = startElem

    ' ==================================================
    ' 3. 遍历处理
    ' ==================================================
    ' ==============================
    ' 3.1. 遍历扫描器，直到扫描器用完
    ' ==============================
    While tokenScanner.hasNext
        ' ==============================
        ' 3.2. 吸收注释
        ' ==============================
        ' 1. 计算偏移量
        ' 1.1. 检查: 如果左括号栈为空，并且遇到第一个非算数运算符需要停止 `needStopInFirstNonArithmetic == true`
        If UtilsStack.isEmptyObjStack(leftParenthesisStack) And needStopInFirstNonArithmetic = True Then
            ' 执行: 获取扫描器到下一个最近的算数运算符的偏移量，结果保存到 `offset`
            offset = SqlFormatterHelper.getNextComputeOperatorOffset(tokenScanner)

        ' 1.2. 否则，执行: 获取扫描器到下一个最近运算符的偏移量，结果保存到 `offset`
        Else
            offset = SqlFormatterHelper.getNextOperatorOffset(tokenScanner)
        End If

        ' 2. 检查: 如果 `offset == 0`，则退出遍历
        If offset = 0 Then
            GoTo whileBreak

        ' 3. 否则，检查: 如果 `offset > 1`
        ElseIf offset > 1 Then
            ' 3.1. 检查: `result` 是空，则创建一个空的容器列对象并保存到 `result`: `SqlFormatterHelper.createContainerCol`
            If parseExprColStartWithOperator Is Nothing Then
                Set parseExprColStartWithOperator = SqlFormatterHelper.createContainerCol
            End If
            ' 3.2. 执行:吸收 offset - 1 个注释元素到 result 右侧，结果保存到 `result`
            Set parseExprColStartWithOperator = consumeComponentToColRight( _
                parseExprColStartWithOperator, tokenScanner, offset - 1)
        End If

        ' ==============================
        ' 3.3. 从扫描器中获取一个 `token`，并获取 token 的类型 `tokenType`
        ' ==============================
        Set token = tokenScanner.peek
        tokenType = token.tokenType

        ' ==============================
        ' 3.3. 匹配运算符及后面的列
        ' ==============================
        ' 1. 处理算数运算符
        ' 1.1. 检查: 如果 token 是算数运算符，则继续
        If SqlHelper.tokenTypeIsComputeOperator(tokenType) Then
            ' 1.2. 将 `result` 转换为表达式列，结果保存到 `result`
            Set parseExprColStartWithOperator = SqlFormatterHelper.transColToExprCol(parseExprColStartWithOperator)
            ' 1.3. 将当前 `token` 保存到 `result`
            parseExprColStartWithOperator.addComponent token
            ' 1.4. 扫描器向前推进
            tokenScanner.toNext
            ' 1.5. 吸收右侧连续出现的注释到列对象，结果保存到 `result`
            Set parseExprColStartWithOperator = consumeCommentToColRight(tokenScanner, parseExprColStartWithOperator)
            ' 1.6. 检查，如果扫描器已经用完，则退出遍历
            If Not tokenScanner.hasNext Then
                GoTo whileBreak
            End If
            ' 1.7. 匹配一个单独的列对象---parseSingleCol，结果保存到 `tempElem`
            Set tempElem = parseSingleCol(tokenScanner, needStopInFirstNonArithmetic)
            ' 1.8. 检查: 如果 `tempElem` 是空，则退出遍历
            If tempElem Is Nothing Then
                GoTo whileBreak
            End If
            ' 1.9. 将 `tempElem` 保存到 `result`
            parseExprColStartWithOperator.addComponent tempElem

        ' 2. 处理逻辑运算符
        ' 2.1. 否则，检查: 如果 token 是逻辑运算符: `SqlHelper.tokenTypeIsLogicOperator`，则继续
        ElseIf SqlHelper.tokenTypeIsLogicOperator(tokenType) Then
            ' 2.2. 扫描器向前推进
            tokenScanner.toNext

            ' 2.3. [匹配一个条件对象--parseCondition](#匹配一个条件对象--parseCondition)， 结果保存到 `tempElem`
            Set tempElem = parseCondition(tokenScanner, Nothing, token)

            ' 2.4. 将 result + tempElem 合并为多条件组合对象，结果保存到 `result`
            Set parseExprColStartWithOperator = mergeTwoElemToMultiConditions(parseExprColStartWithOperator, tempElem)

        ' 3. 处理条件运算符
        ' 3.1. 检查: 如果 token 类型是 Not
        ElseIf tokenType = SqlTokenType.KEY_NOT Then
            ' 3.1.1. 获取Not开头的条件运算符，结果保存到 `tempElem`
            Set tempElem = getNotConditionOp(tokenScanner)
            ' 3.1.2. 检查: 如果 `tempElem` 是空，则退出遍历
            If tempElem Is Nothing Then
                GoTo whileBreak
            End If

            ' 3.1.3. 创建一个条件对象，保存到 `result`: `SqlFormatterHelper.createConditionOnlyOpLeft`
            Set parseExprColStartWithOperator = SqlFormatterHelper.createConditionOnlyOpLeft( _
                parseExprColStartWithOperator, tempElem)
            ' 3.1.4. 执行: 从条件运算符开始匹配一个二元条件对象--parseConditionFromOperator，结果保存到 `result`
            Set parseExprColStartWithOperator = parseConditionFromOperator( _
                tokenScanner, Nothing, parseExprColStartWithOperator, tempElem.getType)

        ' 3.2. 否则，检查: 如果 token 是条件运算符，则继续
        ElseIf SqlHelper.tokenTypeIsConditionOperator(tokenType) Then
            ' 3.2.1. 扫描器向前推进
            tokenScanner.toNext

            ' 3.2.2. 创建一个条件对象，保存到 `result`: `SqlFormatterHelper.createConditionOnlyOpLeft`
            Set parseExprColStartWithOperator = SqlFormatterHelper.createConditionOnlyOpLeft( _
                parseExprColStartWithOperator, token)

            ' 3.2.3. 执行: 从条件运算符开始匹配一个二元条件对象--parseConditionFromOperator，结果保存到 `result`
            Set parseExprColStartWithOperator = parseConditionFromOperator( _
                tokenScanner, Nothing, parseExprColStartWithOperator, token.tokenType)
        ' 4. 其他情况
        Else
            ' 4.1. 退出遍历
            GoTo whileBreak
        End If

        ' 5. 执行: [列对象括号配对](#列对象括号配对)，结果保存到 `result`
        Set parseExprColStartWithOperator = matchColBoundaryParenthesis( _
            parseExprColStartWithOperator, tokenScanner, leftParenthesisStack)
    Wend
whileBreak:

    ' ==================================================
    ' 4. 退出处理返回 `result`
    ' ==================================================
    ' return result
End Function

' 匹配单列名称表达式
'
' @param tokenScanner 扫描器
' @param leftParenthesisStack 左括号栈 (ObjStack<SqlToken>)
' @return 列对象
Public Function parseColExpr( _
    ByRef tokenScanner As ObjArrayRefScanner, _
    ByRef leftParenthesisStack As ObjStack) As ISqlFormatterElem

    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果列对象 `ISqlFormatterElem result = null`
    '    这里直接使用 parseColExpr
    ' 2. token `SqlToken token`
    Dim token As SqlToken
    ' 3. token 类型 `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType

    ' ==================================================
    ' 2. 初始化
    ' ==================================================
    ' 1. 从扫描器中获取一个 `token`，并保存类型到 `tokenType`
    Set token = tokenScanner.peek
    tokenType = token.tokenType

    ' ==================================================
    ' 3. 处理右括号 `)`
    ' ==================================================
    ' 1. 检查: 如果 token 类型是右括号，则继续
    If tokenType = SqlTokenType.RIGHT_PARENTHESIS Then
        ' 2. 检查: 如果左括号栈为空，则退出处理，返回 null
        If UtilsStack.isEmptyObjStack(leftParenthesisStack) Then
            ' return null
            Exit Function
        End If
        ' 3. 执行: 对一个右括号进行配对并生成容器列对象，结果保存到 `result`
        Set parseColExpr = matchOneRightParenthesis(tokenScanner, leftParenthesisStack)

    ' ==================================================
    ' 4. 处理名称
    ' ==================================================
    ' 1. 检查: 如果 token 类型是 `SqlTokenType.STR_NAME`，则继续
    ElseIf tokenType = SqlTokenType.STR_NAME Then
        ' 2. 执行 匹配一个以名称开始的列对象--parseColFromStrName，结果保存到 `result`
        Set parseColExpr = parseColFromStrName(tokenScanner, leftParenthesisStack)

    ' ==================================================
    ' 5. 处理字面量和全数据列
    ' ==================================================
    ' 1. 检查: 如果 token 类型是 `SqlTokenType.ASTERISK`，跳转到: 生成结果
    ElseIf tokenType = SqlTokenType.ASTERISK Then
        tokenScanner.toNext
        Set parseColExpr = matchColBoundaryParenthesis(token, tokenScanner, leftParenthesisStack)

    ' 2. 检查: 如果 token 类型是 `SqlTokenType.STR_NUM`，跳转到: 生成结果
    ElseIf tokenType = SqlTokenType.STR_NUM Then
        tokenScanner.toNext
        Set parseColExpr = matchColBoundaryParenthesis(token, tokenScanner, leftParenthesisStack)

    ' 3. 检查: 如果 token 类型是 `SqlTokenType.STR_STR`，跳转到: 生成结果
    ElseIf tokenType = SqlTokenType.STR_STR Then
        tokenScanner.toNext
        Set parseColExpr = matchColBoundaryParenthesis(token, tokenScanner, leftParenthesisStack)

    ' 4. 检查: 如果 token 类型是 `SqlTokenType.KEY_TRUE`，跳转到: 生成结果
    ElseIf tokenType = SqlTokenType.KEY_TRUE Then
        tokenScanner.toNext
        Set parseColExpr = matchColBoundaryParenthesis(token, tokenScanner, leftParenthesisStack)

    ' 5. 检查: 如果 token 类型是 `SqlTokenType.KEY_FALSE`，跳转到: 生成结果
    ElseIf tokenType = SqlTokenType.KEY_FALSE Then
        tokenScanner.toNext
        Set parseColExpr = matchColBoundaryParenthesis(token, tokenScanner, leftParenthesisStack)

    ' 6. 检查: 如果 token 类型是 `SqlTokenType.KEY_NULL`，跳转到: 生成结果
    ElseIf tokenType = SqlTokenType.KEY_NULL Then
        tokenScanner.toNext
        Set parseColExpr = matchColBoundaryParenthesis(token, tokenScanner, leftParenthesisStack)

    ' 7. 检查: 如果 token 类型是 sql 常量，跳转到: 生成结果:
    ElseIf SqlHelper.tokenTypeIsSqlConst(tokenType) Then
        tokenScanner.toNext
        Set parseColExpr = matchColBoundaryParenthesis(token, tokenScanner, leftParenthesisStack)

    ' ==================================================
    ' 6. 处理函数
    ' ==================================================
    ' 1. 检查: 如果 token 类型是 `SqlTokenType.STR_FUNC`，则继续
    ElseIf tokenType = SqlTokenType.STR_FUNC Then
        ' 2. 匹配函数列对象--parseFunction，结果保存到 `result`
        Set parseColExpr = parseFunction(tokenScanner, leftParenthesisStack)

    ' ==================================================
    ' 7. 处理 case
    ' ==================================================
    ' 1. 检查: 如果 token 类型是 `SqlTokenType.KEY_CASE`，则继续
    ElseIf tokenType = SqlTokenType.KEY_CASE Then
        ' 2. 匹配case表达式--parseCaseStatement，结果保存到 `result`
        Set parseColExpr = parseCaseStatement(tokenScanner, leftParenthesisStack)

    ' ==================================================
    ' 8. 处理 cast
    ' ==================================================
    ' 1. 检查: 如果 token 类型是 `SqlTokenType.KEY_CAST`，则继续
    ElseIf tokenType = SqlTokenType.KEY_CAST Then
        ' 2. 匹配cast函数--parseCastFunc，结果保存到 `result`
        Set parseColExpr = parseCastFunc(tokenScanner, leftParenthesisStack)

    ' ==================================================
    ' 9. 处理子查询
    ' ==================================================
    ' 1. 检查: 如果 token 类型是 `SqlTokenType.KEY_SELECT` 或者 `SqlTokenType.KEY_WITH`
    ElseIf tokenType = SqlTokenType.KEY_SELECT Or tokenType = SqlTokenType.KEY_WITH Then
        ' 2. 执行: [解析sql并生成作用域--处理union](#解析sql并生成作用域--处理union)，结果保存到 `result`
        Set parseColExpr = doParseSqlEnv(tokenScanner, leftParenthesisStack, True)

    ' ==================================================
    ' 10. 处理 `+` 和 `-`
    ' ==================================================
    ' 1. 检查: 如果 token 类型是 `SqlTokenType.OPERA_MINUS` 或者 `SqlTokenType.OPERA_PLUS`，则继续
    ElseIf tokenType = SqlTokenType.OPERA_MINUS Or tokenType = SqlTokenType.OPERA_PLUS Then
        ' 2. 扫描器向前推进
        tokenScanner.toNext
        ' 3. 检查: 如果扫描器已经用完了，则退出处理，返回 `token`
        If Not tokenScanner.hasNext Then
            Set parseColExpr = token
            Exit Function
        End If
        ' 4. 创建一个容器列对象，保存到 `result`
        Set parseColExpr = SqlFormatterHelper.createContainerCol
        ' 5. 将 `token` 添加到 `result`
        parseColExpr.addComponent token
        ' 6. 执行: 匹配任意形式的列表达式--parseMixedColExpr，返回结果直接添加到 `result`
        '         这里遇到一个非算数运算符需要停止
        parseColExpr.addComponent parseMixedColExpr(tokenScanner, True)

        ' 7. 执行: 列对象括号配对，结果保存到 `result`
        Set parseColExpr = matchColBoundaryParenthesis(parseColExpr, tokenScanner, leftParenthesisStack)
    End If
    ' // ==================================================
    ' // 11. 其他情况
    ' // ==================================================
    ' // 1. 不做处理，默认为 `result` 为 null

    ' // ==================================================
    ' // 12. 退出处理，并返回 `result`
    ' // ==================================================
    ' return result
End Function

' 匹配order by
'
' @param tokenScanner 扫描器
' @return SqlFormatterKeySectionEnv
Public Function parseOrderByEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. key `ISqlFormatterElem key`
    Dim key As ISqlFormatterElem
    ' 2. token `SqlToken token`
    Dim token As SqlToken
    ' 3. tokenType `SqlTokenType tokenType`
    Dim tokenType As SqlTokenType
    ' 4. 保存排序列的容器 `ISqlFormatterElem orderItems`
    Dim orderItems As ISqlFormatterElem
    ' 5. 临时元素 `ISqlFormatterElem tempElem`
    Dim tempElem As ISqlFormatterElem
    ' 6. 偏移量 `int offset`
    Dim offset As Integer
    ' 7. 结果对象 `ISqlFormatterElem result`
    '    这里直接使用 parseOrderByEnv

    ' ==================================================
    ' 2. 处理 order by
    ' ==================================================
    ' 1. 从扫描器中弹出一个 token，保存到 `key`
    Set key = tokenScanner.getNext
    ' 2. 为 result 吸收一个同行注释并合并为TokenGroup，结果保存到 `result`    
    Set key = consumeShareLineCommentToTokenGroup(key, tokenScanner)
    ' 3. 检查: 如果扫描器已经用完，则退出处理，并返回 `key`
    If Not tokenScanner.hasNext Then
        ' return key
        Set parseOrderByEnv = key
        Exit Function
    End If

    ' ==================================================
    ' 3. 匹配第一个排序列
    ' ==================================================
    ' ==============================
    ' 3.1. 吸收列开始前的注释
    ' ==============================
    ' 1. 执行: 获取扫描器到下一个最近的不是使匹配筛选项操作停止的sql关键字的偏移量，结果保存到 `offset`
    offset = SqlFormatterHelper.getNextNonSelectItemStopSqlKeyOffset(tokenScanner)
    ' 2. 检查: 如果不存在筛选项 `offset == 0`，退出处理，并返回 `key`
    If offset = 0 Then
        ' return key
        Set parseOrderByEnv = key
        Exit Function
    End If

    ' 3. 创建结果对象
    ' 3.1. 创建排序列的容器: `orderItems = SqlFormatterHelper.createCommaDelimitMultiElem(true)`
    Set orderItems = SqlFormatterHelper.createCommaDelimitMultiElem(True)
    ' 3.2. 生成一个 KeySectionEnv, 并保存为结果对象 `result`
    Set parseOrderByEnv = SqlFormatterHelper.createKeySectionEnv(key, orderItems)

    ' 4. 检查: 如果 `offset > 1`，执行:吸收 offset - 1 个注释元素到 orderItems 右侧
    If offset > 1 Then
        consumeComponentToEnvRight orderItems, tokenScanner, offset - 1
    End If

    ' ==============================
    ' 3.2. 匹配列
    ' ==============================
    ' 1. 执行: 匹配任意形式的列表达式--parseMixedColExpr，结果保存到 `tempElem`
    Set tempElem = parseMixedColExpr(tokenScanner)
    ' 2. 检查: 如果 `tempElem` 是空，则退出处理，并返回 `result`
    If tempElem Is Nothing Then
        ' return key
        Set parseOrderByEnv = key
        Exit Function
    End If
    ' 3. 将 `tempElem` 保存到 `orderItems`
    orderItems.addComponent tempElem
    ' 4. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If

    ' ==============================
    ' 3.3. 匹配排序关键字 (这前面就不再解析注释了)
    ' ==============================
    ' 1. 从扫描器中获取一个 `token`，并保存类型到 `tokenType`
    Set token = tokenScanner.peek
    tokenType = token.tokenType
    ' 2. 检查: 如果 `tokenType` 是 `SqlTokenType.KEY_ASC` 或者 `SqlTokenType.KEY_DESC`，则继续
    If tokenType = SqlTokenType.KEY_ASC Or tokenType = SqlTokenType.KEY_DESC Then
        ' 3. 向前推进扫描器
        tokenScanner.toNext
        ' 4. 将 `token` 保存到 `orderItems`
        orderItems.addComponent token

        ' 5. 检查: 如果扫描器已经用完，则退出处理，并返回 `result`
        If Not tokenScanner.hasNext Then
            ' return result
            Exit Function
        End If
        ' 6. 从扫描器中获取一个 `token`
        Set token = tokenScanner.peek
        ' 7. 检查: 如果 `token` 是一个同行注释
        If token.ISqlFormatterElem_isShareLineComment Then
            ' 7.1. 向前推进扫描器
            tokenScanner.toNext
            ' 7.2. 将 `token` 保存到 `result`
            orderItems.addComponent token
        End If
    End If

    ' ==================================================
    ' 4. 遍历，收集剩余的排序列
    ' ==================================================
    ' 4.1. 遍历扫描器，如果扫描器用完了，则退出遍历
    While tokenScanner.hasNext
        ' ==============================
        ' 4.2. 处理逗号
        ' ==============================
        ' 1. 获取扫描器到下一个最近的逗号的偏移量，结果保存到 `offset`
        offset = SqlFormatterHelper.getNextCommaOffset(tokenScanner)
        ' 2. 检查: 如果 `offset == 0`，退出遍历
        If offset = 0 Then
            GoTo whileBreak

        ' 3. 否则，检查: 如果 `offset > 1`，执行:吸收 offset - 1 个注释元素到 orderItems 右侧
        ElseIf offset > 1 Then
            consumeComponentToEnvRight orderItems, tokenScanner, offset - 1
        End If
        ' 4. 从扫描器中弹出一个 `token` (这里弹出的是逗号)
        Set token = tokenScanner.getNext
        ' 5. 将 `token` 保存到 `orderItems`
        orderItems.addComponent token

        ' ==============================
        ' 4.3. 吸收列开始前的注释
        ' ==============================
        ' 1. 执行: 获取扫描器到下一个最近的不是使匹配筛选项操作停止的sql关键字的偏移量，结果保存到 `offset`
        offset = SqlFormatterHelper.getNextNonSelectItemStopSqlKeyOffset(tokenScanner)
        ' 2. 检查: 如果不存在筛选项 `offset == 0`，退出遍历
        If offset = 0 Then
            GoTo whileBreak

        ' 3. 检查: 如果 `offset > 1`，执行:吸收 offset - 1 个注释元素到 orderItems 右侧
        ElseIf offset > 1 Then
            consumeComponentToEnvRight orderItems, tokenScanner, offset - 1
        End If

        ' ==============================
        ' 4.4. 匹配列
        ' ==============================
        ' 1. 执行: 匹配任意形式的列表达式--parseMixedColExpr，结果保存到 `tempElem`
        Set tempElem = parseMixedColExpr(tokenScanner)
        ' 2. 检查: 如果 `tempElem` 是空，则退出遍历
        If tempElem Is Nothing Then
            GoTo whileBreak
        End If
        ' 3. 将 `tempElem` 保存到 `orderItems`
        orderItems.addComponent tempElem
        ' 4. 检查: 如果扫描器已经用完，则退出遍历
        If Not tokenScanner.hasNext Then
            GoTo whileBreak
        End If

        ' ==============================
        ' 4.5. 匹配排序关键字 (这前面就不再解析注释了)
        ' ==============================
        ' 1. 从扫描器中获取一个 `token`，并保存类型到 `tokenType`
        Set token = tokenScanner.peek
        tokenType = token.tokenType
        ' 2. 检查: 如果 `tokenType` 是 `SqlTokenType.KEY_ASC` 或者 `SqlTokenType.KEY_DESC`，则继续
        If tokenType = SqlTokenType.KEY_ASC Or tokenType = SqlTokenType.KEY_DESC Then
            ' 3. 向前推进扫描器
            tokenScanner.toNext
            ' 4. 将 `token` 保存到 `orderItems`
            orderItems.addComponent token

            ' 5. 检查: 如果扫描器已经用完，则退出遍历
            If Not tokenScanner.hasNext Then
                GoTo whileBreak
            End If
            ' 6. 从扫描器中获取一个 `token`
            Set token = tokenScanner.peek
            ' 7. 检查: 如果 `token` 是一个同行注释
            If token.ISqlFormatterElem_isShareLineComment Then
                ' 7.1. 向前推进扫描器
                tokenScanner.toNext
                ' 7.2. 将 `token` 保存到 `orderItems`
                orderItems.addComponent token
            End If
        End If

        ' ==============================
        ' 4.6. 从 2 开始进入下一次遍历
        ' ==============================
    Wend
whileBreak:

    ' ==================================================
    ' 5. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function

' 匹配 group by
'
' @param tokenScanner 扫描器
' @return SqlFormatterKeySectionEnv
Public Function parseGroupByEnv(ByRef tokenScanner As ObjArrayRefScanner) As ISqlFormatterElem
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 临时元素 `ISqlFormatterElem result`
    '    这里直接使用 parseGroupByEnv
    ' 2. token `SqlToken token01`
    Dim token01 As SqlToken
    ' 3. token `SqlToken token02`
    Dim token02 As SqlToken

    ' ==================================================
    ' 2. 解析 WITH ROLLUP 之前的内容
    ' ==================================================
    ' 1. 执行: 匹配orderby，将结果保存到 `result`
    Set parseGroupByEnv = parseOrderByEnv(tokenScanner)

    ' 2. 检查如果扫描器已经用完，则退出处理，并返回 `result`
    If Not tokenScanner.hasNext Then
        ' return result
        Exit Function
    End If

    ' ==================================================
    ' 3. 匹配 WITH ROLLUP
    ' ==================================================
    ' 1. 从扫描器中获取一个 token 保存到 `token01`
    Set token01 = tokenScanner.peek

    ' 2. 检查: 如果 token 类型不是 with，则退出处理，并返回 `result`
    If token01.tokenType <> SqlTokenType.KEY_WITH Then
        ' return result
        Exit Function
    End If

    ' 3. 扫描器向前推进
    tokenScanner.toNext

    ' 4. 检查: 如果扫描器没用完，则继续
    If tokenScanner.hasNext Then
        ' 5. 从扫描器中获取一个 token 保存到 `token02`
        Set token02 = tokenScanner.peek

        ' 6. 检查: 如果 token 类型不是 roolup
        If token02.tokentype <> SqlTokenType.KEY_ROLLUP Then
            ' 6.1. 将前一个 with 的 token 写回扫描器
            tokenScanner.back 1
            ' 6.2. 退出处理，并返回 `result`
            ' return result
            Exit Function
        End If

        ' 7. 扫描器向前推进
        tokenScanner.toNext
    End If

    ' ==================================================
    ' 4. 保存结果
    ' ==================================================
    ' 1. 检查: 如果 `result` 的类型不是 `SqlFormatterKeySectionEnv`，并且也不是 `SqlFormatterContainerEnv`
    If Not TypeOf parseGroupByEnv Is SqlFormatterKeySectionEnv _
        And Not TypeOf parseGroupByEnv Is SqlFormatterContainerEnv Then
        ' 1.1. 将 `result` 包装为容器作用域，结果保存到 `result`
        Set parseGroupByEnv = SqlFormatterHelper.createKeyStartEnv(parseGroupByEnv, parseGroupByEnv.getType)
    End If

    ' 2. 检查如果 `token02` 不是空，则: 将 `token01` + `token02` 变成 token 组合，并保存到 `result`: `SqlFormatterHelper.createTwoElemTokenGroup`
    If Not token02 Is Nothing Then
        parseGroupByEnv.addComponent SqlFormatterHelper.createTwoElemTokenGroup(token01, token02)

    ' 3. 否则，将 `token01` 保存到 `result`
    '    如果扫描器用完，则会出现只有 with 没有 rollup 的情况
    Else
        parseGroupByEnv.addComponent token01
    End If

    ' ==================================================
    ' 5. 退出处理，返回 `result`
    ' ==================================================
    ' return result
End Function